Automatic sources dropoff on 2020-06-10 18:32:38.095721
The change is generated with prebuilt drop tool.
Change-Id: I24cbf6ba6db262a1ae1445db1427a08fee35b3b4
diff --git a/java/net/AbstractPlainDatagramSocketImpl.java b/java/net/AbstractPlainDatagramSocketImpl.java
new file mode 100644
index 0000000..a3c738a
--- /dev/null
+++ b/java/net/AbstractPlainDatagramSocketImpl.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.net;
+
+import libcore.io.IoBridge;
+import libcore.io.IoUtils;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.security.AccessController;
+
+import dalvik.system.BlockGuard;
+import dalvik.system.CloseGuard;
+import sun.net.ResourceManager;
+
+/**
+ * Abstract datagram and multicast socket implementation base class.
+ * Note: This is not a public class, so that applets cannot call
+ * into the implementation directly and hence cannot bypass the
+ * security checks present in the DatagramSocket and MulticastSocket
+ * classes.
+ *
+ * @author Pavani Diwanji
+ */
+
+abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
+{
+ /* timeout value for receive() */
+ int timeout = 0;
+ boolean connected = false;
+ private int trafficClass = 0;
+ protected InetAddress connectedAddress = null;
+ private int connectedPort = -1;
+
+ // Android-added: CloseGuard
+ private final CloseGuard guard = CloseGuard.get();
+
+ private static final String os = AccessController.doPrivileged(
+ new sun.security.action.GetPropertyAction("os.name")
+ );
+
+ /**
+ * flag set if the native connect() call not to be used
+ */
+ private final static boolean connectDisabled = os.contains("OS X");
+
+ // BEGIN Android-removed: Android doesn't need to load native net library
+ /**
+ * Load net library into runtime.
+ *
+ static {
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("net");
+ return null;
+ }
+ });
+ }
+ */
+ // END Android-removed: Android doesn't need to load native net library
+
+ /**
+ * Creates a datagram socket
+ */
+ protected synchronized void create() throws SocketException {
+ ResourceManager.beforeUdpCreate();
+ fd = new FileDescriptor();
+ try {
+ datagramSocketCreate();
+ } catch (SocketException ioe) {
+ ResourceManager.afterUdpClose();
+ fd = null;
+ throw ioe;
+ }
+
+ // Android-added: CloseGuard/fdsan
+ if (fd != null && fd.valid()) {
+ guard.open("close");
+ IoUtils.setFdOwner(fd, this);
+ }
+ }
+
+ /**
+ * Binds a datagram socket to a local port.
+ */
+ protected synchronized void bind(int lport, InetAddress laddr)
+ throws SocketException {
+ bind0(lport, laddr);
+ }
+
+ protected abstract void bind0(int lport, InetAddress laddr)
+ throws SocketException;
+
+ /**
+ * Sends a datagram packet. The packet contains the data and the
+ * destination address to send the packet to.
+ * @param p the packet to be sent.
+ */
+ protected abstract void send(DatagramPacket p) throws IOException;
+
+ /**
+ * Connects a datagram socket to a remote destination. This associates the remote
+ * address with the local socket so that datagrams may only be sent to this destination
+ * and received from this destination.
+ * @param address the remote InetAddress to connect to
+ * @param port the remote port number
+ */
+ protected void connect(InetAddress address, int port) throws SocketException {
+ // Android-added: BlockGuard
+ BlockGuard.getThreadPolicy().onNetwork();
+ connect0(address, port);
+ connectedAddress = address;
+ connectedPort = port;
+ connected = true;
+ }
+
+ /**
+ * Disconnects a previously connected socket. Does nothing if the socket was
+ * not connected already.
+ */
+ protected void disconnect() {
+ disconnect0(connectedAddress.holder().getFamily());
+ connected = false;
+ connectedAddress = null;
+ connectedPort = -1;
+ }
+
+ /**
+ * Peek at the packet to see who it is from.
+ * @param i the address to populate with the sender address
+ */
+ protected abstract int peek(InetAddress i) throws IOException;
+ protected abstract int peekData(DatagramPacket p) throws IOException;
+ /**
+ * Receive the datagram packet.
+ * @param p the packet to receive into
+ */
+ protected synchronized void receive(DatagramPacket p)
+ throws IOException {
+ receive0(p);
+ }
+
+ protected abstract void receive0(DatagramPacket p)
+ throws IOException;
+
+ /**
+ * Set the TTL (time-to-live) option.
+ * @param ttl TTL to be set.
+ */
+ protected abstract void setTimeToLive(int ttl) throws IOException;
+
+ /**
+ * Get the TTL (time-to-live) option.
+ */
+ protected abstract int getTimeToLive() throws IOException;
+
+ /**
+ * Set the TTL (time-to-live) option.
+ * @param ttl TTL to be set.
+ */
+ @Deprecated
+ protected abstract void setTTL(byte ttl) throws IOException;
+
+ /**
+ * Get the TTL (time-to-live) option.
+ */
+ @Deprecated
+ protected abstract byte getTTL() throws IOException;
+
+ /**
+ * Join the multicast group.
+ * @param inetaddr multicast address to join.
+ */
+ protected void join(InetAddress inetaddr) throws IOException {
+ join(inetaddr, null);
+ }
+
+ /**
+ * Leave the multicast group.
+ * @param inetaddr multicast address to leave.
+ */
+ protected void leave(InetAddress inetaddr) throws IOException {
+ leave(inetaddr, null);
+ }
+ /**
+ * Join the multicast group.
+ * @param mcastaddr multicast address to join.
+ * @param netIf specifies the local interface to receive multicast
+ * datagram packets
+ * @throws IllegalArgumentException if mcastaddr is null or is a
+ * SocketAddress subclass not supported by this socket
+ * @since 1.4
+ */
+
+ protected void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf)
+ throws IOException {
+ if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress))
+ throw new IllegalArgumentException("Unsupported address type");
+ join(((InetSocketAddress)mcastaddr).getAddress(), netIf);
+ }
+
+ protected abstract void join(InetAddress inetaddr, NetworkInterface netIf)
+ throws IOException;
+
+ /**
+ * Leave the multicast group.
+ * @param mcastaddr multicast address to leave.
+ * @param netIf specified the local interface to leave the group at
+ * @throws IllegalArgumentException if mcastaddr is null or is a
+ * SocketAddress subclass not supported by this socket
+ * @since 1.4
+ */
+ protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf)
+ throws IOException {
+ if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress))
+ throw new IllegalArgumentException("Unsupported address type");
+ leave(((InetSocketAddress)mcastaddr).getAddress(), netIf);
+ }
+
+ protected abstract void leave(InetAddress inetaddr, NetworkInterface netIf)
+ throws IOException;
+
+ /**
+ * Close the socket.
+ */
+ protected void close() {
+ // Android-added: CloseGuard
+ guard.close();
+
+ if (fd != null) {
+ datagramSocketClose();
+ ResourceManager.afterUdpClose();
+ fd = null;
+ }
+ }
+
+ protected boolean isClosed() {
+ return (fd == null) ? true : false;
+ }
+
+ protected void finalize() {
+ // Android-added: CloseGuard
+ if (guard != null) {
+ guard.warnIfOpen();
+ }
+
+ close();
+ }
+
+ /**
+ * set a value - since we only support (setting) binary options
+ * here, o must be a Boolean
+ */
+
+ public void setOption(int optID, Object o) throws SocketException {
+ if (isClosed()) {
+ throw new SocketException("Socket Closed");
+ }
+ switch (optID) {
+ /* check type safety b4 going native. These should never
+ * fail, since only java.Socket* has access to
+ * PlainSocketImpl.setOption().
+ */
+ case SO_TIMEOUT:
+ if (o == null || !(o instanceof Integer)) {
+ throw new SocketException("bad argument for SO_TIMEOUT");
+ }
+ int tmp = ((Integer) o).intValue();
+ if (tmp < 0)
+ throw new IllegalArgumentException("timeout < 0");
+ timeout = tmp;
+ return;
+ case IP_TOS:
+ if (o == null || !(o instanceof Integer)) {
+ throw new SocketException("bad argument for IP_TOS");
+ }
+ trafficClass = ((Integer)o).intValue();
+ break;
+ case SO_REUSEADDR:
+ if (o == null || !(o instanceof Boolean)) {
+ throw new SocketException("bad argument for SO_REUSEADDR");
+ }
+ break;
+ case SO_BROADCAST:
+ if (o == null || !(o instanceof Boolean)) {
+ throw new SocketException("bad argument for SO_BROADCAST");
+ }
+ break;
+ case SO_BINDADDR:
+ throw new SocketException("Cannot re-bind Socket");
+ case SO_RCVBUF:
+ case SO_SNDBUF:
+ if (o == null || !(o instanceof Integer) ||
+ ((Integer)o).intValue() < 0) {
+ throw new SocketException("bad argument for SO_SNDBUF or " +
+ "SO_RCVBUF");
+ }
+ break;
+ case IP_MULTICAST_IF:
+ if (o == null || !(o instanceof InetAddress))
+ throw new SocketException("bad argument for IP_MULTICAST_IF");
+ break;
+ case IP_MULTICAST_IF2:
+ // Android-changed: Support Integer IP_MULTICAST_IF2 values for app compat. b/26790580
+ // if (o == null || !(o instanceof NetworkInterface))
+ if (o == null || !(o instanceof Integer || o instanceof NetworkInterface))
+ throw new SocketException("bad argument for IP_MULTICAST_IF2");
+ if (o instanceof NetworkInterface) {
+ o = new Integer(((NetworkInterface)o).getIndex());
+ }
+ break;
+ case IP_MULTICAST_LOOP:
+ if (o == null || !(o instanceof Boolean))
+ throw new SocketException("bad argument for IP_MULTICAST_LOOP");
+ break;
+ default:
+ throw new SocketException("invalid option: " + optID);
+ }
+ socketSetOption(optID, o);
+ }
+
+ /*
+ * get option's state - set or not
+ */
+
+ public Object getOption(int optID) throws SocketException {
+ if (isClosed()) {
+ throw new SocketException("Socket Closed");
+ }
+
+ Object result;
+
+ switch (optID) {
+ case SO_TIMEOUT:
+ result = new Integer(timeout);
+ break;
+
+ case IP_TOS:
+ result = socketGetOption(optID);
+ if ( ((Integer)result).intValue() == -1) {
+ result = new Integer(trafficClass);
+ }
+ break;
+
+ case SO_BINDADDR:
+ case IP_MULTICAST_IF:
+ case IP_MULTICAST_IF2:
+ case SO_RCVBUF:
+ case SO_SNDBUF:
+ case IP_MULTICAST_LOOP:
+ case SO_REUSEADDR:
+ case SO_BROADCAST:
+ result = socketGetOption(optID);
+ // Android-added: Added for app compat reason. See methodgetNIFirstAddress
+ if (optID == IP_MULTICAST_IF) {
+ return getNIFirstAddress((Integer)result);
+ }
+ break;
+
+ default:
+ throw new SocketException("invalid option: " + optID);
+ }
+
+ return result;
+ }
+
+ // BEGIN Android-added: Support Integer IP_MULTICAST_IF2 values for app compat. b/26790580
+ // Native code is changed to return the index of network interface when calling
+ // getOption(IP_MULTICAST_IF2) due to app compat reason.
+ //
+ // For getOption(IP_MULTICAST_IF), we should keep returning InetAddress instance. This method
+ // convert NetworkInterface index into InetAddress instance.
+ /** Return the first address bound to NetworkInterface with given ID.
+ * In case of niIndex == 0 or no address return anyLocalAddress
+ */
+ static InetAddress getNIFirstAddress(int niIndex) throws SocketException {
+ if (niIndex > 0) {
+ NetworkInterface networkInterface = NetworkInterface.getByIndex(niIndex);
+ Enumeration<InetAddress> addressesEnum = networkInterface.getInetAddresses();
+ if (addressesEnum.hasMoreElements()) {
+ return addressesEnum.nextElement();
+ }
+ }
+ return InetAddress.anyLocalAddress();
+ }
+ // END Android-added: Support Integer IP_MULTICAST_IF2 values for app compat. b/26790580
+
+ protected abstract void datagramSocketCreate() throws SocketException;
+ protected abstract void datagramSocketClose();
+ protected abstract void socketSetOption(int opt, Object val)
+ throws SocketException;
+ protected abstract Object socketGetOption(int opt) throws SocketException;
+
+ protected abstract void connect0(InetAddress address, int port) throws SocketException;
+ protected abstract void disconnect0(int family);
+
+ protected boolean nativeConnectDisabled() {
+ return connectDisabled;
+ }
+
+ // Android-changed: rewritten on the top of IoBridge
+ int dataAvailable() {
+ try {
+ return IoBridge.available(fd);
+ } catch (IOException e) {
+ return -1;
+ }
+ }
+}
diff --git a/java/net/AbstractPlainSocketImpl.java b/java/net/AbstractPlainSocketImpl.java
new file mode 100644
index 0000000..0500811
--- /dev/null
+++ b/java/net/AbstractPlainSocketImpl.java
@@ -0,0 +1,794 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.FileDescriptor;
+
+import dalvik.annotation.optimization.ReachabilitySensitive;
+import dalvik.system.BlockGuard;
+import dalvik.system.CloseGuard;
+import dalvik.system.SocketTagger;
+import sun.net.ConnectionResetException;
+import sun.net.NetHooks;
+import sun.net.ResourceManager;
+
+/**
+ * Default Socket Implementation. This implementation does
+ * not implement any security checks.
+ * Note this class should <b>NOT</b> be public.
+ *
+ * @author Steven B. Byrne
+ */
+abstract class AbstractPlainSocketImpl extends SocketImpl
+{
+ /* instance variable for SO_TIMEOUT */
+ int timeout; // timeout in millisec
+ // Android-removed: traffic class is set through socket
+ // private int trafficClass;
+
+ private boolean shut_rd = false;
+ private boolean shut_wr = false;
+
+ private SocketInputStream socketInputStream = null;
+ private SocketOutputStream socketOutputStream = null;
+
+ /* number of threads using the FileDescriptor */
+ protected int fdUseCount = 0;
+
+ /* lock when increment/decrementing fdUseCount */
+ // Android-added: @ReachabilitySensitive
+ // Marked mostly because it's used where fd is, and fd isn't declared here.
+ // This adds reachabilityFences where we would if fd were annotated.
+ @ReachabilitySensitive
+ protected final Object fdLock = new Object();
+
+ /* indicates a close is pending on the file descriptor */
+ protected boolean closePending = false;
+
+ /* indicates connection reset state */
+ private int CONNECTION_NOT_RESET = 0;
+ private int CONNECTION_RESET_PENDING = 1;
+ private int CONNECTION_RESET = 2;
+ private int resetState;
+ private final Object resetLock = new Object();
+
+ /* whether this Socket is a stream (TCP) socket or not (UDP)
+ */
+ protected boolean stream;
+
+ // BEGIN Android-removed: Android doesn't need to load native net library
+ /*
+ /**
+ * Load net library into runtime.
+ *
+ static {
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("net");
+ return null;
+ }
+ });
+ }
+ */
+ // END Android-removed: Android doesn't need to load native net library
+
+ // Android-added: logs a warning if socket is not closed
+ @ReachabilitySensitive
+ private final CloseGuard guard = CloseGuard.get();
+
+ /**
+ * Creates a socket with a boolean that specifies whether this
+ * is a stream socket (true) or an unconnected UDP socket (false).
+ */
+ protected synchronized void create(boolean stream) throws IOException {
+ this.stream = stream;
+ if (!stream) {
+ ResourceManager.beforeUdpCreate();
+ // Android-removed: socketCreate should set fd if it succeeds
+ // fd = new FileDescriptor();
+ try {
+ socketCreate(false);
+ } catch (IOException ioe) {
+ ResourceManager.afterUdpClose();
+ // Android-removed: b/26470377 Represent closed sockets with invalid fd, not null.
+ // fd = null;
+ throw ioe;
+ }
+ } else {
+ // Android-removed: socketCreate should set fd if it succeeds
+ // fd = new FileDescriptor();
+ socketCreate(true);
+ }
+ if (socket != null)
+ socket.setCreated();
+ if (serverSocket != null)
+ serverSocket.setCreated();
+
+ // Android-added: CloseGuard
+ if (fd != null && fd.valid()) {
+ guard.open("close");
+ }
+ }
+
+ /**
+ * Creates a socket and connects it to the specified port on
+ * the specified host.
+ * @param host the specified host
+ * @param port the specified port
+ */
+ protected void connect(String host, int port)
+ throws UnknownHostException, IOException
+ {
+ boolean connected = false;
+ try {
+ InetAddress address = InetAddress.getByName(host);
+ this.port = port;
+ this.address = address;
+
+ connectToAddress(address, port, timeout);
+ connected = true;
+ } finally {
+ if (!connected) {
+ try {
+ close();
+ } catch (IOException ioe) {
+ /* Do nothing. If connect threw an exception then
+ it will be passed up the call stack */
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a socket and connects it to the specified address on
+ * the specified port.
+ * @param address the address
+ * @param port the specified port
+ */
+ protected void connect(InetAddress address, int port) throws IOException {
+ this.port = port;
+ this.address = address;
+
+ try {
+ connectToAddress(address, port, timeout);
+ return;
+ } catch (IOException e) {
+ // everything failed
+ close();
+ throw e;
+ }
+ }
+
+ /**
+ * Creates a socket and connects it to the specified address on
+ * the specified port.
+ * @param address the address
+ * @param timeout the timeout value in milliseconds, or zero for no timeout.
+ * @throws IOException if connection fails
+ * @throws IllegalArgumentException if address is null or is a
+ * SocketAddress subclass not supported by this socket
+ * @since 1.4
+ */
+ protected void connect(SocketAddress address, int timeout)
+ throws IOException {
+ boolean connected = false;
+ try {
+ if (address == null || !(address instanceof InetSocketAddress))
+ throw new IllegalArgumentException("unsupported address type");
+ InetSocketAddress addr = (InetSocketAddress) address;
+ if (addr.isUnresolved())
+ throw new UnknownHostException(addr.getHostName());
+ this.port = addr.getPort();
+ this.address = addr.getAddress();
+
+ connectToAddress(this.address, port, timeout);
+ connected = true;
+ } finally {
+ if (!connected) {
+ try {
+ close();
+ } catch (IOException ioe) {
+ /* Do nothing. If connect threw an exception then
+ it will be passed up the call stack */
+ }
+ }
+ }
+ }
+
+ private void connectToAddress(InetAddress address, int port, int timeout) throws IOException {
+ if (address.isAnyLocalAddress()) {
+ doConnect(InetAddress.getLocalHost(), port, timeout);
+ } else {
+ doConnect(address, port, timeout);
+ }
+ }
+
+ public void setOption(int opt, Object val) throws SocketException {
+ if (isClosedOrPending()) {
+ throw new SocketException("Socket Closed");
+ }
+ // BEGIN Android-removed: Logic dealing with value type moved to socketSetOption.
+ /*
+ boolean on = true;
+ switch (opt) {
+ /* check type safety b4 going native. These should never
+ * fail, since only java.Socket* has access to
+ * PlainSocketImpl.setOption().
+ *
+ case SO_LINGER:
+ if (val == null || (!(val instanceof Integer) && !(val instanceof Boolean)))
+ throw new SocketException("Bad parameter for option");
+ if (val instanceof Boolean) {
+ /* true only if disabling - enabling should be Integer *
+ on = false;
+ }
+ break;
+ case SO_TIMEOUT:
+ if (val == null || (!(val instanceof Integer)))
+ throw new SocketException("Bad parameter for SO_TIMEOUT");
+ int tmp = ((Integer) val).intValue();
+ if (tmp < 0)
+ throw new IllegalArgumentException("timeout < 0");
+ timeout = tmp;
+ break;
+ case IP_TOS:
+ if (val == null || !(val instanceof Integer)) {
+ throw new SocketException("bad argument for IP_TOS");
+ }
+ trafficClass = ((Integer)val).intValue();
+ break;
+ case SO_BINDADDR:
+ throw new SocketException("Cannot re-bind socket");
+ case TCP_NODELAY:
+ if (val == null || !(val instanceof Boolean))
+ throw new SocketException("bad parameter for TCP_NODELAY");
+ on = ((Boolean)val).booleanValue();
+ break;
+ case SO_SNDBUF:
+ case SO_RCVBUF:
+ if (val == null || !(val instanceof Integer) ||
+ !(((Integer)val).intValue() > 0)) {
+ throw new SocketException("bad parameter for SO_SNDBUF " +
+ "or SO_RCVBUF");
+ }
+ break;
+ case SO_KEEPALIVE:
+ if (val == null || !(val instanceof Boolean))
+ throw new SocketException("bad parameter for SO_KEEPALIVE");
+ on = ((Boolean)val).booleanValue();
+ break;
+ case SO_OOBINLINE:
+ if (val == null || !(val instanceof Boolean))
+ throw new SocketException("bad parameter for SO_OOBINLINE");
+ on = ((Boolean)val).booleanValue();
+ break;
+ case SO_REUSEADDR:
+ if (val == null || !(val instanceof Boolean))
+ throw new SocketException("bad parameter for SO_REUSEADDR");
+ on = ((Boolean)val).booleanValue();
+ break;
+ default:
+ throw new SocketException("unrecognized TCP option: " + opt);
+ }
+ socketSetOption(opt, on, val);
+ */
+ // END Android-removed: Logic dealing with value type moved to socketSetOption.
+ // Android-added: Keep track of timeout value not handled by socketSetOption
+ if (opt == SO_TIMEOUT) {
+ timeout = (Integer) val;
+ }
+ socketSetOption(opt, val);
+ }
+ public Object getOption(int opt) throws SocketException {
+ if (isClosedOrPending()) {
+ throw new SocketException("Socket Closed");
+ }
+ if (opt == SO_TIMEOUT) {
+ return new Integer(timeout);
+ }
+ // BEGIN Android-changed: Logic dealing with value type moved to socketGetOption.
+ /*
+ int ret = 0;
+ /*
+ * The native socketGetOption() knows about 3 options.
+ * The 32 bit value it returns will be interpreted according
+ * to what we're asking. A return of -1 means it understands
+ * the option but its turned off. It will raise a SocketException
+ * if "opt" isn't one it understands.
+ *
+
+ switch (opt) {
+ case TCP_NODELAY:
+ ret = socketGetOption(opt, null);
+ return Boolean.valueOf(ret != -1);
+ case SO_OOBINLINE:
+ ret = socketGetOption(opt, null);
+ return Boolean.valueOf(ret != -1);
+ case SO_LINGER:
+ ret = socketGetOption(opt, null);
+ return (ret == -1) ? Boolean.FALSE: (Object)(new Integer(ret));
+ case SO_REUSEADDR:
+ ret = socketGetOption(opt, null);
+ return Boolean.valueOf(ret != -1);
+ case SO_BINDADDR:
+ InetAddressContainer in = new InetAddressContainer();
+ ret = socketGetOption(opt, in);
+ return in.addr;
+ case SO_SNDBUF:
+ case SO_RCVBUF:
+ ret = socketGetOption(opt, null);
+ return new Integer(ret);
+ case IP_TOS:
+ try {
+ ret = socketGetOption(opt, null);
+ if (ret == -1) { // ipv6 tos
+ return trafficClass;
+ } else {
+ return ret;
+ }
+ } catch (SocketException se) {
+ // TODO - should make better effort to read TOS or TCLASS
+ return trafficClass; // ipv6 tos
+ }
+ case SO_KEEPALIVE:
+ ret = socketGetOption(opt, null);
+ return Boolean.valueOf(ret != -1);
+ // should never get here
+ default:
+ return null;
+ }
+ */
+ return socketGetOption(opt);
+ // END Android-changed: Logic dealing with value type moved to socketGetOption.
+ }
+
+ /**
+ * The workhorse of the connection operation. Tries several times to
+ * establish a connection to the given <host, port>. If unsuccessful,
+ * throws an IOException indicating what went wrong.
+ */
+
+ synchronized void doConnect(InetAddress address, int port, int timeout) throws IOException {
+ synchronized (fdLock) {
+ if (!closePending && (socket == null || !socket.isBound())) {
+ NetHooks.beforeTcpConnect(fd, address, port);
+ }
+ }
+ try {
+ acquireFD();
+ try {
+ // Android-added: BlockGuard
+ BlockGuard.getThreadPolicy().onNetwork();
+ socketConnect(address, port, timeout);
+ /* socket may have been closed during poll/select */
+ synchronized (fdLock) {
+ if (closePending) {
+ throw new SocketException ("Socket closed");
+ }
+ }
+ // If we have a ref. to the Socket, then sets the flags
+ // created, bound & connected to true.
+ // This is normally done in Socket.connect() but some
+ // subclasses of Socket may call impl.connect() directly!
+ if (socket != null) {
+ socket.setBound();
+ socket.setConnected();
+ }
+ } finally {
+ releaseFD();
+ }
+ } catch (IOException e) {
+ close();
+ throw e;
+ }
+ }
+
+ /**
+ * Binds the socket to the specified address of the specified local port.
+ * @param address the address
+ * @param lport the port
+ */
+ protected synchronized void bind(InetAddress address, int lport)
+ throws IOException
+ {
+ synchronized (fdLock) {
+ if (!closePending && (socket == null || !socket.isBound())) {
+ NetHooks.beforeTcpBind(fd, address, lport);
+ }
+ }
+ socketBind(address, lport);
+ if (socket != null)
+ socket.setBound();
+ if (serverSocket != null)
+ serverSocket.setBound();
+ }
+
+ /**
+ * Listens, for a specified amount of time, for connections.
+ * @param count the amount of time to listen for connections
+ */
+ protected synchronized void listen(int count) throws IOException {
+ socketListen(count);
+ }
+
+ /**
+ * Accepts connections.
+ * @param s the connection
+ */
+ protected void accept(SocketImpl s) throws IOException {
+ acquireFD();
+ try {
+ // Android-added: BlockGuard
+ BlockGuard.getThreadPolicy().onNetwork();
+ socketAccept(s);
+ } finally {
+ releaseFD();
+ }
+ }
+
+ /**
+ * Gets an InputStream for this socket.
+ */
+ protected synchronized InputStream getInputStream() throws IOException {
+ synchronized (fdLock) {
+ if (isClosedOrPending())
+ throw new IOException("Socket Closed");
+ if (shut_rd)
+ throw new IOException("Socket input is shutdown");
+ if (socketInputStream == null)
+ socketInputStream = new SocketInputStream(this);
+ }
+ return socketInputStream;
+ }
+
+ void setInputStream(SocketInputStream in) {
+ socketInputStream = in;
+ }
+
+ /**
+ * Gets an OutputStream for this socket.
+ */
+ protected synchronized OutputStream getOutputStream() throws IOException {
+ synchronized (fdLock) {
+ if (isClosedOrPending())
+ throw new IOException("Socket Closed");
+ if (shut_wr)
+ throw new IOException("Socket output is shutdown");
+ if (socketOutputStream == null)
+ socketOutputStream = new SocketOutputStream(this);
+ }
+ return socketOutputStream;
+ }
+
+ // Android-removed: this.fd is maintained by the concrete implementation.
+ /*
+ void setFileDescriptor(FileDescriptor fd) {
+ this.fd = fd;
+ }
+ */
+
+ void setAddress(InetAddress address) {
+ this.address = address;
+ }
+
+ void setPort(int port) {
+ this.port = port;
+ }
+
+ void setLocalPort(int localport) {
+ this.localport = localport;
+ }
+
+ /**
+ * Returns the number of bytes that can be read without blocking.
+ */
+ protected synchronized int available() throws IOException {
+ if (isClosedOrPending()) {
+ throw new IOException("Stream closed.");
+ }
+
+ /*
+ * If connection has been reset or shut down for input, then return 0
+ * to indicate there are no buffered bytes.
+ */
+ if (isConnectionReset() || shut_rd) {
+ return 0;
+ }
+
+ /*
+ * If no bytes available and we were previously notified
+ * of a connection reset then we move to the reset state.
+ *
+ * If are notified of a connection reset then check
+ * again if there are bytes buffered on the socket.
+ */
+ int n = 0;
+ try {
+ n = socketAvailable();
+ if (n == 0 && isConnectionResetPending()) {
+ setConnectionReset();
+ }
+ } catch (ConnectionResetException exc1) {
+ setConnectionResetPending();
+ try {
+ n = socketAvailable();
+ if (n == 0) {
+ setConnectionReset();
+ }
+ } catch (ConnectionResetException exc2) {
+ }
+ }
+ return n;
+ }
+
+ /**
+ * Closes the socket.
+ */
+ protected void close() throws IOException {
+ synchronized(fdLock) {
+ if (fd != null && fd.valid()) {
+ if (!stream) {
+ ResourceManager.afterUdpClose();
+ }
+ // Android-changed:
+ // Socket should be untagged before the preclose. After preclose,
+ // socket will dup2-ed to marker_fd, therefore, it won't describe the same file.
+ // If closingPending is true, then the socket has been preclosed.
+ //
+ // Also, close the CloseGuard when the #close is called.
+ if (!closePending) {
+ closePending = true;
+ guard.close();
+
+ if (fdUseCount == 0) {
+ /*
+ * We close the FileDescriptor in two-steps - first the
+ * "pre-close" which closes the socket but doesn't
+ * release the underlying file descriptor. This operation
+ * may be lengthy due to untransmitted data and a long
+ * linger interval. Once the pre-close is done we do the
+ * actual socket to release the fd.
+ */
+ try {
+ socketPreClose();
+ } finally {
+ socketClose();
+ }
+ // Android-changed(http://b/26470377): Some Android code doesn't expect file
+ // descriptor to be null. socketClose invalidates the fd by closing the fd.
+ // fd = null;
+ return;
+ } else {
+ /*
+ * If a thread has acquired the fd and a close
+ * isn't pending then use a deferred close.
+ * Also decrement fdUseCount to signal the last
+ * thread that releases the fd to close it.
+ */
+ fdUseCount--;
+ socketPreClose();
+ }
+ }
+ }
+ }
+ }
+
+ void reset() throws IOException {
+ if (fd != null && fd.valid()) {
+ socketClose();
+ // Android-changed: Notified the CloseGuard object as the fd has been released.
+ guard.close();
+ }
+ // Android-removed: b/26470377 Represent closed sockets with invalid fd, not null.
+ // fd = null;
+ super.reset();
+ }
+
+
+ /**
+ * Shutdown read-half of the socket connection;
+ */
+ protected void shutdownInput() throws IOException {
+ // Android-changed: b/26470377 Represent closed sockets with invalid fd, not null.
+ if (fd != null && fd.valid()) {
+ socketShutdown(SHUT_RD);
+ if (socketInputStream != null) {
+ socketInputStream.setEOF(true);
+ }
+ shut_rd = true;
+ }
+ }
+
+ /**
+ * Shutdown write-half of the socket connection;
+ */
+ protected void shutdownOutput() throws IOException {
+ // Android-changed: b/26470377 Represent closed sockets with invalid fd, not null.
+ if (fd != null && fd.valid()) {
+ socketShutdown(SHUT_WR);
+ shut_wr = true;
+ }
+ }
+
+ protected boolean supportsUrgentData () {
+ return true;
+ }
+
+ protected void sendUrgentData (int data) throws IOException {
+ // Android-changed: b/26470377 Represent closed sockets with invalid fd, not null.
+ if (fd == null || !fd.valid()) {
+ throw new IOException("Socket Closed");
+ }
+ socketSendUrgentData (data);
+ }
+
+ /**
+ * Cleans up if the user forgets to close it.
+ */
+ protected void finalize() throws IOException {
+ // Android-added: CloseGuard
+ if (guard != null) {
+ guard.warnIfOpen();
+ }
+
+ close();
+ }
+
+ /*
+ * "Acquires" and returns the FileDescriptor for this impl
+ *
+ * A corresponding releaseFD is required to "release" the
+ * FileDescriptor.
+ */
+ FileDescriptor acquireFD() {
+ synchronized (fdLock) {
+ fdUseCount++;
+ return fd;
+ }
+ }
+
+ /*
+ * "Release" the FileDescriptor for this impl.
+ *
+ * If the use count goes to -1 then the socket is closed.
+ */
+ void releaseFD() {
+ synchronized (fdLock) {
+ fdUseCount--;
+ if (fdUseCount == -1) {
+ if (fd != null) {
+ try {
+ socketClose();
+ } catch (IOException e) {
+ // Android-removed: b/26470377 Some Android code doesn't expect file
+ // descriptor to be null. socketClose invalidates the fd by closing the fd.
+ // } finally {
+ // fd = null;
+ }
+ }
+ }
+ }
+ }
+
+ public boolean isConnectionReset() {
+ synchronized (resetLock) {
+ return (resetState == CONNECTION_RESET);
+ }
+ }
+
+ public boolean isConnectionResetPending() {
+ synchronized (resetLock) {
+ return (resetState == CONNECTION_RESET_PENDING);
+ }
+ }
+
+ public void setConnectionReset() {
+ synchronized (resetLock) {
+ resetState = CONNECTION_RESET;
+ }
+ }
+
+ public void setConnectionResetPending() {
+ synchronized (resetLock) {
+ if (resetState == CONNECTION_NOT_RESET) {
+ resetState = CONNECTION_RESET_PENDING;
+ }
+ }
+
+ }
+
+ /*
+ * Return true if already closed or close is pending
+ */
+ public boolean isClosedOrPending() {
+ /*
+ * Lock on fdLock to ensure that we wait if a
+ * close is in progress.
+ */
+ synchronized (fdLock) {
+ // Android-changed: b/26470377 Represent closed sockets with invalid fd, not null.
+ if (closePending || (fd == null) || !fd.valid()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /*
+ * Return the current value of SO_TIMEOUT
+ */
+ public int getTimeout() {
+ return timeout;
+ }
+
+ /*
+ * "Pre-close" a socket by dup'ing the file descriptor - this enables
+ * the socket to be closed without releasing the file descriptor.
+ */
+ private void socketPreClose() throws IOException {
+ socketClose0(true);
+ }
+
+ /*
+ * Close the socket (and release the file descriptor).
+ */
+ protected void socketClose() throws IOException {
+ socketClose0(false);
+ }
+
+ abstract void socketCreate(boolean isServer) throws IOException;
+ abstract void socketConnect(InetAddress address, int port, int timeout)
+ throws IOException;
+ abstract void socketBind(InetAddress address, int port)
+ throws IOException;
+ abstract void socketListen(int count)
+ throws IOException;
+ abstract void socketAccept(SocketImpl s)
+ throws IOException;
+ abstract int socketAvailable()
+ throws IOException;
+ abstract void socketClose0(boolean useDeferredClose)
+ throws IOException;
+ abstract void socketShutdown(int howto)
+ throws IOException;
+
+ // Android-changed: Method signature changed, socket{Get,Set}Option work directly with Object
+ // values.
+ abstract void socketSetOption(int cmd, Object value) throws SocketException;
+ abstract Object socketGetOption(int opt) throws SocketException;
+
+ abstract void socketSendUrgentData(int data)
+ throws IOException;
+
+ public final static int SHUT_RD = 0;
+ public final static int SHUT_WR = 1;
+}
diff --git a/java/net/AddressCache.java b/java/net/AddressCache.java
new file mode 100644
index 0000000..3026923
--- /dev/null
+++ b/java/net/AddressCache.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2010 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 android.compat.annotation.UnsupportedAppUsage;
+import libcore.util.BasicLruCache;
+
+/**
+ * Implements caching for {@code InetAddress}. We use a unified cache for both positive and negative
+ * cache entries.
+ *
+ * TODO: benchmark and optimize InetAddress until we get to the point where we can just rely on
+ * the C library level caching. The main thing caching at this level buys us is avoiding repeated
+ * conversions from 'struct sockaddr's to InetAddress[].
+ */
+class AddressCache {
+ /**
+ * When the cache contains more entries than this, we start dropping the oldest ones.
+ * This should be a power of two to avoid wasted space in our custom map.
+ */
+ private static final int MAX_ENTRIES = 16;
+
+ // The TTL for the Java-level cache is short, just 2s.
+ private static final long TTL_NANOS = 2 * 1000000000L;
+
+ // The actual cache.
+ @UnsupportedAppUsage
+ private final BasicLruCache<AddressCacheKey, AddressCacheEntry> cache
+ = new BasicLruCache<AddressCacheKey, AddressCacheEntry>(MAX_ENTRIES);
+
+ static class AddressCacheKey {
+ @UnsupportedAppUsage
+ private final String mHostname;
+ private final int mNetId;
+
+ AddressCacheKey(String hostname, int netId) {
+ mHostname = hostname;
+ mNetId = netId;
+ }
+
+ @Override public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof AddressCacheKey)) {
+ return false;
+ }
+ AddressCacheKey lhs = (AddressCacheKey) o;
+ return mHostname.equals(lhs.mHostname) && mNetId == lhs.mNetId;
+ }
+
+ @Override public int hashCode() {
+ int result = 17;
+ result = 31 * result + mNetId;
+ result = 31 * result + mHostname.hashCode();
+ return result;
+ }
+ }
+
+ static class AddressCacheEntry {
+ // Either an InetAddress[] for a positive entry,
+ // or a String detail message for a negative entry.
+ @UnsupportedAppUsage
+ final Object value;
+
+ /**
+ * The absolute expiry time in nanoseconds. Nanoseconds from System.nanoTime is ideal
+ * because -- unlike System.currentTimeMillis -- it can never go backwards.
+ *
+ * We don't need to worry about overflow with a TTL_NANOS of 2s.
+ */
+ @UnsupportedAppUsage
+ final long expiryNanos;
+
+ @UnsupportedAppUsage
+ AddressCacheEntry(Object value) {
+ this.value = value;
+ this.expiryNanos = System.nanoTime() + TTL_NANOS;
+ }
+ }
+
+ /**
+ * Removes all entries from the cache.
+ */
+ public void clear() {
+ cache.evictAll();
+ }
+
+ /**
+ * Returns the cached InetAddress[] for 'hostname' on network 'netId'. Returns null
+ * if nothing is known about 'hostname'. Returns a String suitable for use as an
+ * UnknownHostException detail message if 'hostname' is known not to exist.
+ */
+ public Object get(String hostname, int netId) {
+ AddressCacheEntry entry = cache.get(new AddressCacheKey(hostname, netId));
+ // Do we have a valid cache entry?
+ if (entry != null && entry.expiryNanos >= System.nanoTime()) {
+ return entry.value;
+ }
+ // Either we didn't find anything, or it had expired.
+ // No need to remove expired entries: the caller will provide a replacement shortly.
+ return null;
+ }
+
+ /**
+ * Associates the given 'addresses' with 'hostname'. The association will expire after a
+ * certain length of time.
+ */
+ public void put(String hostname, int netId, InetAddress[] addresses) {
+ cache.put(new AddressCacheKey(hostname, netId), new AddressCacheEntry(addresses));
+ }
+
+ /**
+ * Records that 'hostname' is known not to have any associated addresses. (I.e. insert a
+ * negative cache entry.)
+ */
+ public void putUnknownHost(String hostname, int netId, String detailMessage) {
+ cache.put(new AddressCacheKey(hostname, netId), new AddressCacheEntry(detailMessage));
+ }
+}
diff --git a/java/net/Authenticator.java b/java/net/Authenticator.java
new file mode 100644
index 0000000..c88a600
--- /dev/null
+++ b/java/net/Authenticator.java
@@ -0,0 +1,425 @@
+/*
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * The class Authenticator represents an object that knows how to obtain
+ * authentication for a network connection. Usually, it will do this
+ * by prompting the user for information.
+ * <p>
+ * Applications use this class by overriding {@link
+ * #getPasswordAuthentication()} in a sub-class. This method will
+ * typically use the various getXXX() accessor methods to get information
+ * about the entity requesting authentication. It must then acquire a
+ * username and password either by interacting with the user or through
+ * some other non-interactive means. The credentials are then returned
+ * as a {@link PasswordAuthentication} return value.
+ * <p>
+ * An instance of this concrete sub-class is then registered
+ * with the system by calling {@link #setDefault(Authenticator)}.
+ * When authentication is required, the system will invoke one of the
+ * requestPasswordAuthentication() methods which in turn will call the
+ * getPasswordAuthentication() method of the registered object.
+ * <p>
+ * All methods that request authentication have a default implementation
+ * that fails.
+ *
+ * @see java.net.Authenticator#setDefault(java.net.Authenticator)
+ * @see java.net.Authenticator#getPasswordAuthentication()
+ *
+ * @author Bill Foote
+ * @since 1.2
+ */
+
+// There are no abstract methods, but to be useful the user must
+// subclass.
+public abstract
+class Authenticator {
+
+ // The system-wide authenticator object. See setDefault().
+ private static Authenticator theAuthenticator;
+
+ private String requestingHost;
+ private InetAddress requestingSite;
+ private int requestingPort;
+ private String requestingProtocol;
+ private String requestingPrompt;
+ private String requestingScheme;
+ private URL requestingURL;
+ private RequestorType requestingAuthType;
+
+ /**
+ * The type of the entity requesting authentication.
+ *
+ * @since 1.5
+ */
+ public enum RequestorType {
+ /**
+ * Entity requesting authentication is a HTTP proxy server.
+ */
+ PROXY,
+ /**
+ * Entity requesting authentication is a HTTP origin server.
+ */
+ SERVER
+ }
+
+ private void reset() {
+ requestingHost = null;
+ requestingSite = null;
+ requestingPort = -1;
+ requestingProtocol = null;
+ requestingPrompt = null;
+ requestingScheme = null;
+ requestingURL = null;
+ requestingAuthType = RequestorType.SERVER;
+ }
+
+
+ /**
+ * Sets the authenticator that will be used by the networking code
+ * when a proxy or an HTTP server asks for authentication.
+ * <p>
+ * First, if there is a security manager, its {@code checkPermission}
+ * method is called with a
+ * {@code NetPermission("setDefaultAuthenticator")} permission.
+ * This may result in a java.lang.SecurityException.
+ *
+ * @param a The authenticator to be set. If a is {@code null} then
+ * any previously set authenticator is removed.
+ *
+ * @throws SecurityException
+ * if a security manager exists and its
+ * {@code checkPermission} method doesn't allow
+ * setting the default authenticator.
+ *
+ * @see SecurityManager#checkPermission
+ * @see java.net.NetPermission
+ */
+ public synchronized static void setDefault(Authenticator a) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ NetPermission setDefaultPermission
+ = new NetPermission("setDefaultAuthenticator");
+ sm.checkPermission(setDefaultPermission);
+ }
+
+ theAuthenticator = a;
+ }
+
+ /**
+ * Ask the authenticator that has been registered with the system
+ * for a password.
+ * <p>
+ * First, if there is a security manager, its {@code checkPermission}
+ * method is called with a
+ * {@code NetPermission("requestPasswordAuthentication")} permission.
+ * This may result in a java.lang.SecurityException.
+ *
+ * @param addr The InetAddress of the site requesting authorization,
+ * or null if not known.
+ * @param port the port for the requested connection
+ * @param protocol The protocol that's requesting the connection
+ * ({@link java.net.Authenticator#getRequestingProtocol()})
+ * @param prompt A prompt string for the user
+ * @param scheme The authentication scheme
+ *
+ * @return The username/password, or null if one can't be gotten.
+ *
+ * @throws SecurityException
+ * if a security manager exists and its
+ * {@code checkPermission} method doesn't allow
+ * the password authentication request.
+ *
+ * @see SecurityManager#checkPermission
+ * @see java.net.NetPermission
+ */
+ public static PasswordAuthentication requestPasswordAuthentication(
+ InetAddress addr,
+ int port,
+ String protocol,
+ String prompt,
+ String scheme) {
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ NetPermission requestPermission
+ = new NetPermission("requestPasswordAuthentication");
+ sm.checkPermission(requestPermission);
+ }
+
+ Authenticator a = theAuthenticator;
+ if (a == null) {
+ return null;
+ } else {
+ synchronized(a) {
+ a.reset();
+ a.requestingSite = addr;
+ a.requestingPort = port;
+ a.requestingProtocol = protocol;
+ a.requestingPrompt = prompt;
+ a.requestingScheme = scheme;
+ return a.getPasswordAuthentication();
+ }
+ }
+ }
+
+ /**
+ * Ask the authenticator that has been registered with the system
+ * for a password. This is the preferred method for requesting a password
+ * because the hostname can be provided in cases where the InetAddress
+ * is not available.
+ * <p>
+ * First, if there is a security manager, its {@code checkPermission}
+ * method is called with a
+ * {@code NetPermission("requestPasswordAuthentication")} permission.
+ * This may result in a java.lang.SecurityException.
+ *
+ * @param host The hostname of the site requesting authentication.
+ * @param addr The InetAddress of the site requesting authentication,
+ * or null if not known.
+ * @param port the port for the requested connection.
+ * @param protocol The protocol that's requesting the connection
+ * ({@link java.net.Authenticator#getRequestingProtocol()})
+ * @param prompt A prompt string for the user which identifies the authentication realm.
+ * @param scheme The authentication scheme
+ *
+ * @return The username/password, or null if one can't be gotten.
+ *
+ * @throws SecurityException
+ * if a security manager exists and its
+ * {@code checkPermission} method doesn't allow
+ * the password authentication request.
+ *
+ * @see SecurityManager#checkPermission
+ * @see java.net.NetPermission
+ * @since 1.4
+ */
+ public static PasswordAuthentication requestPasswordAuthentication(
+ String host,
+ InetAddress addr,
+ int port,
+ String protocol,
+ String prompt,
+ String scheme) {
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ NetPermission requestPermission
+ = new NetPermission("requestPasswordAuthentication");
+ sm.checkPermission(requestPermission);
+ }
+
+ Authenticator a = theAuthenticator;
+ if (a == null) {
+ return null;
+ } else {
+ synchronized(a) {
+ a.reset();
+ a.requestingHost = host;
+ a.requestingSite = addr;
+ a.requestingPort = port;
+ a.requestingProtocol = protocol;
+ a.requestingPrompt = prompt;
+ a.requestingScheme = scheme;
+ return a.getPasswordAuthentication();
+ }
+ }
+ }
+
+ /**
+ * Ask the authenticator that has been registered with the system
+ * for a password.
+ * <p>
+ * First, if there is a security manager, its {@code checkPermission}
+ * method is called with a
+ * {@code NetPermission("requestPasswordAuthentication")} permission.
+ * This may result in a java.lang.SecurityException.
+ *
+ * @param host The hostname of the site requesting authentication.
+ * @param addr The InetAddress of the site requesting authorization,
+ * or null if not known.
+ * @param port the port for the requested connection
+ * @param protocol The protocol that's requesting the connection
+ * ({@link java.net.Authenticator#getRequestingProtocol()})
+ * @param prompt A prompt string for the user
+ * @param scheme The authentication scheme
+ * @param url The requesting URL that caused the authentication
+ * @param reqType The type (server or proxy) of the entity requesting
+ * authentication.
+ *
+ * @return The username/password, or null if one can't be gotten.
+ *
+ * @throws SecurityException
+ * if a security manager exists and its
+ * {@code checkPermission} method doesn't allow
+ * the password authentication request.
+ *
+ * @see SecurityManager#checkPermission
+ * @see java.net.NetPermission
+ *
+ * @since 1.5
+ */
+ public static PasswordAuthentication requestPasswordAuthentication(
+ String host,
+ InetAddress addr,
+ int port,
+ String protocol,
+ String prompt,
+ String scheme,
+ URL url,
+ RequestorType reqType) {
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ NetPermission requestPermission
+ = new NetPermission("requestPasswordAuthentication");
+ sm.checkPermission(requestPermission);
+ }
+
+ Authenticator a = theAuthenticator;
+ if (a == null) {
+ return null;
+ } else {
+ synchronized(a) {
+ a.reset();
+ a.requestingHost = host;
+ a.requestingSite = addr;
+ a.requestingPort = port;
+ a.requestingProtocol = protocol;
+ a.requestingPrompt = prompt;
+ a.requestingScheme = scheme;
+ a.requestingURL = url;
+ a.requestingAuthType = reqType;
+ return a.getPasswordAuthentication();
+ }
+ }
+ }
+
+ /**
+ * Gets the {@code hostname} of the
+ * site or proxy requesting authentication, or {@code null}
+ * if not available.
+ *
+ * @return the hostname of the connection requiring authentication, or null
+ * if it's not available.
+ * @since 1.4
+ */
+ protected final String getRequestingHost() {
+ return requestingHost;
+ }
+
+ /**
+ * Gets the {@code InetAddress} of the
+ * site requesting authorization, or {@code null}
+ * if not available.
+ *
+ * @return the InetAddress of the site requesting authorization, or null
+ * if it's not available.
+ */
+ protected final InetAddress getRequestingSite() {
+ return requestingSite;
+ }
+
+ /**
+ * Gets the port number for the requested connection.
+ * @return an {@code int} indicating the
+ * port for the requested connection.
+ */
+ protected final int getRequestingPort() {
+ return requestingPort;
+ }
+
+ /**
+ * Give the protocol that's requesting the connection. Often this
+ * will be based on a URL, but in a future JDK it could be, for
+ * example, "SOCKS" for a password-protected SOCKS5 firewall.
+ *
+ * @return the protocol, optionally followed by "/version", where
+ * version is a version number.
+ *
+ * @see java.net.URL#getProtocol()
+ */
+ protected final String getRequestingProtocol() {
+ return requestingProtocol;
+ }
+
+ /**
+ * Gets the prompt string given by the requestor.
+ *
+ * @return the prompt string given by the requestor (realm for
+ * http requests)
+ */
+ protected final String getRequestingPrompt() {
+ return requestingPrompt;
+ }
+
+ /**
+ * Gets the scheme of the requestor (the HTTP scheme
+ * for an HTTP firewall, for example).
+ *
+ * @return the scheme of the requestor
+ *
+ */
+ protected final String getRequestingScheme() {
+ return requestingScheme;
+ }
+
+ /**
+ * Called when password authorization is needed. Subclasses should
+ * override the default implementation, which returns null.
+ * @return The PasswordAuthentication collected from the
+ * user, or null if none is provided.
+ */
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return null;
+ }
+
+ /**
+ * Returns the URL that resulted in this
+ * request for authentication.
+ *
+ * @since 1.5
+ *
+ * @return the requesting URL
+ *
+ */
+ protected URL getRequestingURL () {
+ return requestingURL;
+ }
+
+ /**
+ * Returns whether the requestor is a Proxy or a Server.
+ *
+ * @since 1.5
+ *
+ * @return the authentication type of the requestor
+ *
+ */
+ protected RequestorType getRequestorType () {
+ return requestingAuthType;
+ }
+}
diff --git a/java/net/BindException.java b/java/net/BindException.java
new file mode 100644
index 0000000..9c2bb48
--- /dev/null
+++ b/java/net/BindException.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * Signals that an error occurred while attempting to bind a
+ * socket to a local address and port. Typically, the port is
+ * in use, or the requested local address could not be assigned.
+ *
+ * @since JDK1.1
+ */
+
+public class BindException extends SocketException {
+ private static final long serialVersionUID = -5945005768251722951L;
+
+ /**
+ * Constructs a new BindException with the specified detail
+ * message as to why the bind error occurred.
+ * A detail message is a String that gives a specific
+ * description of this error.
+ * @param msg the detail message
+ */
+ public BindException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Construct a new BindException with no detailed message.
+ */
+ public BindException() {}
+
+ // Android-added: Constructor called by IoBridge
+ /** @hide */
+ public BindException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
diff --git a/java/net/CacheRequest.java b/java/net/CacheRequest.java
new file mode 100644
index 0000000..a6fbbc3
--- /dev/null
+++ b/java/net/CacheRequest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * Represents channels for storing resources in the
+ * ResponseCache. Instances of such a class provide an
+ * OutputStream object which is called by protocol handlers to
+ * store the resource data into the cache, and also an abort() method
+ * which allows a cache store operation to be interrupted and
+ * abandoned. If an IOException is encountered while reading the
+ * response or writing to the cache, the current cache store operation
+ * will be aborted.
+ *
+ * @author Yingxian Wang
+ * @since 1.5
+ */
+public abstract class CacheRequest {
+
+ /**
+ * Returns an OutputStream to which the response body can be
+ * written.
+ *
+ * @return an OutputStream to which the response body can
+ * be written
+ * @throws IOException if an I/O error occurs while
+ * writing the response body
+ */
+ public abstract OutputStream getBody() throws IOException;
+
+ /**
+ * Aborts the attempt to cache the response. If an IOException is
+ * encountered while reading the response or writing to the cache,
+ * the current cache store operation will be abandoned.
+ */
+ public abstract void abort();
+}
diff --git a/java/net/CacheResponse.java b/java/net/CacheResponse.java
new file mode 100644
index 0000000..272b3d9
--- /dev/null
+++ b/java/net/CacheResponse.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.InputStream;
+import java.util.Map;
+import java.util.List;
+import java.io.IOException;
+
+/**
+ * Represent channels for retrieving resources from the
+ * ResponseCache. Instances of such a class provide an
+ * InputStream that returns the entity body, and also a
+ * getHeaders() method which returns the associated response headers.
+ *
+ * @author Yingxian Wang
+ * @since 1.5
+ */
+public abstract class CacheResponse {
+
+ /**
+ * Returns the response headers as a Map.
+ *
+ * @return An immutable Map from response header field names to
+ * lists of field values. The status line has null as its
+ * field name.
+ * @throws IOException if an I/O error occurs
+ * while getting the response headers
+ */
+ public abstract Map<String, List<String>> getHeaders() throws IOException;
+
+ /**
+ * Returns the response body as an InputStream.
+ *
+ * @return an InputStream from which the response body can
+ * be accessed
+ * @throws IOException if an I/O error occurs while
+ * getting the response body
+ */
+ public abstract InputStream getBody() throws IOException;
+}
diff --git a/java/net/ConnectException.java b/java/net/ConnectException.java
new file mode 100644
index 0000000..ff58643
--- /dev/null
+++ b/java/net/ConnectException.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * Signals that an error occurred while attempting to connect a
+ * socket to a remote address and port. Typically, the connection
+ * was refused remotely (e.g., no process is listening on the
+ * remote address/port).
+ *
+ * @since JDK1.1
+ */
+public class ConnectException extends SocketException {
+ private static final long serialVersionUID = 3831404271622369215L;
+
+ /**
+ * Constructs a new ConnectException with the specified detail
+ * message as to why the connect error occurred.
+ * A detail message is a String that gives a specific
+ * description of this error.
+ * @param msg the detail message
+ */
+ public ConnectException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Construct a new ConnectException with no detailed message.
+ */
+ public ConnectException() {}
+
+ // Android-added: Constructor called by IoBridge
+ /** @hide */
+ public ConnectException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
diff --git a/java/net/ContentHandler.java b/java/net/ContentHandler.java
new file mode 100644
index 0000000..c658585
--- /dev/null
+++ b/java/net/ContentHandler.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+
+/**
+ * The abstract class {@code ContentHandler} is the superclass
+ * of all classes that read an {@code Object} from a
+ * {@code URLConnection}.
+ * <p>
+ * An application does not generally call the
+ * {@code getContent} method in this class directly. Instead, an
+ * application calls the {@code getContent} method in class
+ * {@code URL} or in {@code URLConnection}.
+ * The application's content handler factory (an instance of a class that
+ * implements the interface {@code ContentHandlerFactory} set
+ * up by a call to {@code setContentHandler}) is
+ * called with a {@code String} giving the MIME type of the
+ * object being received on the socket. The factory returns an
+ * instance of a subclass of {@code ContentHandler}, and its
+ * {@code getContent} method is called to create the object.
+ * <p>
+ * If no content handler could be found, URLConnection will
+ * look for a content handler in a user-defineable set of places.
+ * By default it looks in sun.net.www.content, but users can define a
+ * vertical-bar delimited set of class prefixes to search through in
+ * addition by defining the java.content.handler.pkgs property.
+ * The class name must be of the form:
+ * <pre>
+ * {package-prefix}.{major}.{minor}
+ * e.g.
+ * YoyoDyne.experimental.text.plain
+ * </pre>
+ * If the loading of the content handler class would be performed by
+ * a classloader that is outside of the delegation chain of the caller,
+ * the JVM will need the RuntimePermission "getClassLoader".
+ *
+ * @author James Gosling
+ * @see java.net.ContentHandler#getContent(java.net.URLConnection)
+ * @see java.net.ContentHandlerFactory
+ * @see java.net.URL#getContent()
+ * @see java.net.URLConnection
+ * @see java.net.URLConnection#getContent()
+ * @see java.net.URLConnection#setContentHandlerFactory(java.net.ContentHandlerFactory)
+ * @since JDK1.0
+ */
+abstract public class ContentHandler {
+ /**
+ * Given a URL connect stream positioned at the beginning of the
+ * representation of an object, this method reads that stream and
+ * creates an object from it.
+ *
+ * @param urlc a URL connection.
+ * @return the object read by the {@code ContentHandler}.
+ * @exception IOException if an I/O error occurs while reading the object.
+ */
+ abstract public Object getContent(URLConnection urlc) throws IOException;
+
+ /**
+ * Given a URL connect stream positioned at the beginning of the
+ * representation of an object, this method reads that stream and
+ * creates an object that matches one of the types specified.
+ *
+ * The default implementation of this method should call getContent()
+ * and screen the return type for a match of the suggested types.
+ *
+ * @param urlc a URL connection.
+ * @param classes an array of types requested
+ * @return the object read by the {@code ContentHandler} that is
+ * the first match of the suggested types.
+ * null if none of the requested are supported.
+ * @exception IOException if an I/O error occurs while reading the object.
+ * @since 1.3
+ */
+ @SuppressWarnings("rawtypes")
+ public Object getContent(URLConnection urlc, Class[] classes) throws IOException {
+ Object obj = getContent(urlc);
+
+ for (int i = 0; i < classes.length; i++) {
+ if (classes[i].isInstance(obj)) {
+ return obj;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/java/net/ContentHandlerFactory.java b/java/net/ContentHandlerFactory.java
new file mode 100644
index 0000000..64112e3
--- /dev/null
+++ b/java/net/ContentHandlerFactory.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * This interface defines a factory for content handlers. An
+ * implementation of this interface should map a MIME type into an
+ * instance of {@code ContentHandler}.
+ * <p>
+ * This interface is used by the {@code URLStreamHandler} class
+ * to create a {@code ContentHandler} for a MIME type.
+ *
+ * @author James Gosling
+ * @see java.net.ContentHandler
+ * @see java.net.URLStreamHandler
+ * @since JDK1.0
+ */
+public interface ContentHandlerFactory {
+ /**
+ * Creates a new {@code ContentHandler} to read an object from
+ * a {@code URLStreamHandler}.
+ *
+ * @param mimetype the MIME type for which a content handler is desired.
+
+ * @return a new {@code ContentHandler} to read an object from a
+ * {@code URLStreamHandler}.
+ * @see java.net.ContentHandler
+ * @see java.net.URLStreamHandler
+ */
+ ContentHandler createContentHandler(String mimetype);
+}
diff --git a/java/net/CookieHandler.java b/java/net/CookieHandler.java
new file mode 100644
index 0000000..ef91d00
--- /dev/null
+++ b/java/net/CookieHandler.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.util.Map;
+import java.util.List;
+import java.io.IOException;
+import sun.security.util.SecurityConstants;
+
+/**
+ * A CookieHandler object provides a callback mechanism to hook up a
+ * HTTP state management policy implementation into the HTTP protocol
+ * handler. The HTTP state management mechanism specifies a way to
+ * create a stateful session with HTTP requests and responses.
+ *
+ * <p>A system-wide CookieHandler that to used by the HTTP protocol
+ * handler can be registered by doing a
+ * CookieHandler.setDefault(CookieHandler). The currently registered
+ * CookieHandler can be retrieved by calling
+ * CookieHandler.getDefault().
+ *
+ * For more information on HTTP state management, see <a
+ * href="http://www.ietf.org/rfc/rfc2965.txt"><i>RFC 2965: HTTP
+ * State Management Mechanism</i></a>
+ *
+ * @author Yingxian Wang
+ * @since 1.5
+ */
+public abstract class CookieHandler {
+ /**
+ * The system-wide cookie handler that will apply cookies to the
+ * request headers and manage cookies from the response headers.
+ *
+ * @see setDefault(CookieHandler)
+ * @see getDefault()
+ */
+ private static CookieHandler cookieHandler;
+
+ /**
+ * Gets the system-wide cookie handler.
+ *
+ * @return the system-wide cookie handler; A null return means
+ * there is no system-wide cookie handler currently set.
+ * @throws SecurityException
+ * If a security manager has been installed and it denies
+ * {@link NetPermission}{@code ("getCookieHandler")}
+ * @see #setDefault(CookieHandler)
+ */
+ public synchronized static CookieHandler getDefault() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(SecurityConstants.GET_COOKIEHANDLER_PERMISSION);
+ }
+ return cookieHandler;
+ }
+
+ /**
+ * Sets (or unsets) the system-wide cookie handler.
+ *
+ * Note: non-standard http protocol handlers may ignore this setting.
+ *
+ * @param cHandler The HTTP cookie handler, or
+ * {@code null} to unset.
+ * @throws SecurityException
+ * If a security manager has been installed and it denies
+ * {@link NetPermission}{@code ("setCookieHandler")}
+ * @see #getDefault()
+ */
+ public synchronized static void setDefault(CookieHandler cHandler) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(SecurityConstants.SET_COOKIEHANDLER_PERMISSION);
+ }
+ cookieHandler = cHandler;
+ }
+
+ /**
+ * Gets all the applicable cookies from a cookie cache for the
+ * specified uri in the request header.
+ *
+ * <P>The {@code URI} passed as an argument specifies the intended use for
+ * the cookies. In particular the scheme should reflect whether the cookies
+ * will be sent over http, https or used in another context like javascript.
+ * The host part should reflect either the destination of the cookies or
+ * their origin in the case of javascript.</P>
+ * <P>It is up to the implementation to take into account the {@code URI} and
+ * the cookies attributes and security settings to determine which ones
+ * should be returned.</P>
+ *
+ * <P>HTTP protocol implementers should make sure that this method is
+ * called after all request headers related to choosing cookies
+ * are added, and before the request is sent.</P>
+ *
+ * @param uri a {@code URI} representing the intended use for the
+ * cookies
+ * @param requestHeaders - a Map from request header
+ * field names to lists of field values representing
+ * the current request headers
+ * @return an immutable map from state management headers, with
+ * field names "Cookie" or "Cookie2" to a list of
+ * cookies containing state information
+ *
+ * @throws IOException if an I/O error occurs
+ * @throws IllegalArgumentException if either argument is null
+ * @see #put(URI, Map)
+ */
+ public abstract Map<String, List<String>>
+ get(URI uri, Map<String, List<String>> requestHeaders)
+ throws IOException;
+
+ /**
+ * Sets all the applicable cookies, examples are response header
+ * fields that are named Set-Cookie2, present in the response
+ * headers into a cookie cache.
+ *
+ * @param uri a {@code URI} where the cookies come from
+ * @param responseHeaders an immutable map from field names to
+ * lists of field values representing the response
+ * header fields returned
+ * @throws IOException if an I/O error occurs
+ * @throws IllegalArgumentException if either argument is null
+ * @see #get(URI, Map)
+ */
+ public abstract void
+ put(URI uri, Map<String, List<String>> responseHeaders)
+ throws IOException;
+}
diff --git a/java/net/CookieManager.java b/java/net/CookieManager.java
new file mode 100644
index 0000000..dfd3b86
--- /dev/null
+++ b/java/net/CookieManager.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.util.Map;
+import java.util.List;
+import java.util.Collections;
+import java.util.Comparator;
+import java.io.IOException;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * CookieManager provides a concrete implementation of {@link CookieHandler},
+ * which separates the storage of cookies from the policy surrounding accepting
+ * and rejecting cookies. A CookieManager is initialized with a {@link CookieStore}
+ * which manages storage, and a {@link CookiePolicy} object, which makes
+ * policy decisions on cookie acceptance/rejection.
+ *
+ * <p> The HTTP cookie management in java.net package looks like:
+ * <blockquote>
+ * <pre>{@code
+ * use
+ * CookieHandler <------- HttpURLConnection
+ * ^
+ * | impl
+ * | use
+ * CookieManager -------> CookiePolicy
+ * | use
+ * |--------> HttpCookie
+ * | ^
+ * | | use
+ * | use |
+ * |--------> CookieStore
+ * ^
+ * | impl
+ * |
+ * Internal in-memory implementation
+ * }</pre>
+ * <ul>
+ * <li>
+ * CookieHandler is at the core of cookie management. User can call
+ * CookieHandler.setDefault to set a concrete CookieHanlder implementation
+ * to be used.
+ * </li>
+ * <li>
+ * CookiePolicy.shouldAccept will be called by CookieManager.put to see whether
+ * or not one cookie should be accepted and put into cookie store. User can use
+ * any of three pre-defined CookiePolicy, namely ACCEPT_ALL, ACCEPT_NONE and
+ * ACCEPT_ORIGINAL_SERVER, or user can define his own CookiePolicy implementation
+ * and tell CookieManager to use it.
+ * </li>
+ * <li>
+ * CookieStore is the place where any accepted HTTP cookie is stored in.
+ * If not specified when created, a CookieManager instance will use an internal
+ * in-memory implementation. Or user can implements one and tell CookieManager
+ * to use it.
+ * </li>
+ * <li>
+ * Currently, only CookieStore.add(URI, HttpCookie) and CookieStore.get(URI)
+ * are used by CookieManager. Others are for completeness and might be needed
+ * by a more sophisticated CookieStore implementation, e.g. a NetscapeCookieSotre.
+ * </li>
+ * </ul>
+ * </blockquote>
+ *
+ * <p>There're various ways user can hook up his own HTTP cookie management behavior, e.g.
+ * <blockquote>
+ * <ul>
+ * <li>Use CookieHandler.setDefault to set a brand new {@link CookieHandler} implementation
+ * <li>Let CookieManager be the default {@link CookieHandler} implementation,
+ * but implement user's own {@link CookieStore} and {@link CookiePolicy}
+ * and tell default CookieManager to use them:
+ * <blockquote><pre>
+ * // this should be done at the beginning of an HTTP session
+ * CookieHandler.setDefault(new CookieManager(new MyCookieStore(), new MyCookiePolicy()));
+ * </pre></blockquote>
+ * <li>Let CookieManager be the default {@link CookieHandler} implementation, but
+ * use customized {@link CookiePolicy}:
+ * <blockquote><pre>
+ * // this should be done at the beginning of an HTTP session
+ * CookieHandler.setDefault(new CookieManager());
+ * // this can be done at any point of an HTTP session
+ * ((CookieManager)CookieHandler.getDefault()).setCookiePolicy(new MyCookiePolicy());
+ * </pre></blockquote>
+ * </ul>
+ * </blockquote>
+ *
+ * <p>The implementation conforms to <a href="http://www.ietf.org/rfc/rfc2965.txt">RFC 2965</a>, section 3.3.
+ *
+ * @see CookiePolicy
+ * @author Edward Wang
+ * @since 1.6
+ */
+public class CookieManager extends CookieHandler
+{
+ /* ---------------- Fields -------------- */
+
+ private CookiePolicy policyCallback;
+
+
+ private CookieStore cookieJar = null;
+
+
+ /* ---------------- Ctors -------------- */
+
+ /**
+ * Create a new cookie manager.
+ *
+ * <p>This constructor will create new cookie manager with default
+ * cookie store and accept policy. The effect is same as
+ * {@code CookieManager(null, null)}.
+ */
+ public CookieManager() {
+ this(null, null);
+ }
+
+
+ /**
+ * Create a new cookie manager with specified cookie store and cookie policy.
+ *
+ * @param store a {@code CookieStore} to be used by cookie manager.
+ * if {@code null}, cookie manager will use a default one,
+ * which is an in-memory CookieStore implementation.
+ * @param cookiePolicy a {@code CookiePolicy} instance
+ * to be used by cookie manager as policy callback.
+ * if {@code null}, ACCEPT_ORIGINAL_SERVER will
+ * be used.
+ */
+ public CookieManager(CookieStore store,
+ CookiePolicy cookiePolicy)
+ {
+ // use default cookie policy if not specify one
+ policyCallback = (cookiePolicy == null) ? CookiePolicy.ACCEPT_ORIGINAL_SERVER
+ : cookiePolicy;
+
+ // if not specify CookieStore to use, use default one
+ if (store == null) {
+ cookieJar = new InMemoryCookieStore();
+ } else {
+ cookieJar = store;
+ }
+ }
+
+
+ /* ---------------- Public operations -------------- */
+
+ /**
+ * To set the cookie policy of this cookie manager.
+ *
+ * <p> A instance of {@code CookieManager} will have
+ * cookie policy ACCEPT_ORIGINAL_SERVER by default. Users always
+ * can call this method to set another cookie policy.
+ *
+ * @param cookiePolicy the cookie policy. Can be {@code null}, which
+ * has no effects on current cookie policy.
+ */
+ public void setCookiePolicy(CookiePolicy cookiePolicy) {
+ if (cookiePolicy != null) policyCallback = cookiePolicy;
+ }
+
+
+ /**
+ * To retrieve current cookie store.
+ *
+ * @return the cookie store currently used by cookie manager.
+ */
+ public CookieStore getCookieStore() {
+ return cookieJar;
+ }
+
+
+ public Map<String, List<String>>
+ get(URI uri, Map<String, List<String>> requestHeaders)
+ throws IOException
+ {
+ // pre-condition check
+ if (uri == null || requestHeaders == null) {
+ throw new IllegalArgumentException("Argument is null");
+ }
+
+ Map<String, List<String>> cookieMap =
+ new java.util.HashMap<String, List<String>>();
+ // if there's no default CookieStore, no way for us to get any cookie
+ if (cookieJar == null)
+ return Collections.unmodifiableMap(cookieMap);
+
+ boolean secureLink = "https".equalsIgnoreCase(uri.getScheme());
+ List<HttpCookie> cookies = new java.util.ArrayList<HttpCookie>();
+ // BEGIN Android-removed: The logic of converting null path is moved into pathMatches.
+ /*
+ String path = uri.getPath();
+ if (path == null || path.isEmpty()) {
+ path = "/";
+ }
+ */
+ // END Android-removed: The logic of converting null path is moved into pathMatches.
+ for (HttpCookie cookie : cookieJar.get(uri)) {
+ // apply path-matches rule (RFC 2965 sec. 3.3.4)
+ // and check for the possible "secure" tag (i.e. don't send
+ // 'secure' cookies over unsecure links)
+ if (pathMatches(uri, cookie) &&
+ (secureLink || !cookie.getSecure())) {
+ // BEGIN Android-removed: App compat: b/25897688 InMemoryCookieStore ignores scheme.
+ /*
+ if (cookie.isHttpOnly()) {
+ String s = uri.getScheme();
+ if (!"http".equalsIgnoreCase(s) && !"https".equalsIgnoreCase(s)) {
+ continue;
+ }
+ }
+ */
+ // END Android-removed: App compat: b/25897688 InMemoryCookieStore ignores scheme.
+
+ // Let's check the authorize port list if it exists
+ String ports = cookie.getPortlist();
+ if (ports != null && !ports.isEmpty()) {
+ int port = uri.getPort();
+ if (port == -1) {
+ port = "https".equals(uri.getScheme()) ? 443 : 80;
+ }
+ if (isInPortList(ports, port)) {
+ cookies.add(cookie);
+ }
+ } else {
+ cookies.add(cookie);
+ }
+ }
+ }
+ // Android-added: b/25897688 A fix to return empty map if cookies list is empty
+ if (cookies.isEmpty()) {
+ return Collections.emptyMap();
+ }
+
+ // apply sort rule (RFC 2965 sec. 3.3.4)
+ List<String> cookieHeader = sortByPath(cookies);
+
+ cookieMap.put("Cookie", cookieHeader);
+ return Collections.unmodifiableMap(cookieMap);
+ }
+
+ public void
+ put(URI uri, Map<String, List<String>> responseHeaders)
+ throws IOException
+ {
+ // pre-condition check
+ if (uri == null || responseHeaders == null) {
+ throw new IllegalArgumentException("Argument is null");
+ }
+
+
+ // if there's no default CookieStore, no need to remember any cookie
+ if (cookieJar == null)
+ return;
+
+ PlatformLogger logger = PlatformLogger.getLogger("java.net.CookieManager");
+ for (String headerKey : responseHeaders.keySet()) {
+ // RFC 2965 3.2.2, key must be 'Set-Cookie2'
+ // we also accept 'Set-Cookie' here for backward compatibility
+ if (headerKey == null
+ || !(headerKey.equalsIgnoreCase("Set-Cookie2")
+ || headerKey.equalsIgnoreCase("Set-Cookie")
+ )
+ )
+ {
+ continue;
+ }
+
+ for (String headerValue : responseHeaders.get(headerKey)) {
+ try {
+ List<HttpCookie> cookies;
+ try {
+ cookies = HttpCookie.parse(headerValue);
+ } catch (IllegalArgumentException e) {
+ // Bogus header, make an empty list and log the error
+ cookies = java.util.Collections.emptyList();
+ if (logger.isLoggable(PlatformLogger.Level.SEVERE)) {
+ logger.severe("Invalid cookie for " + uri + ": " + headerValue);
+ }
+ }
+ for (HttpCookie cookie : cookies) {
+ if (cookie.getPath() == null) {
+ // If no path is specified, then by default
+ // the path is the directory of the page/doc
+ String path = uri.getPath();
+ if (!path.endsWith("/")) {
+ int i = path.lastIndexOf("/");
+ if (i > 0) {
+ path = path.substring(0, i + 1);
+ } else {
+ path = "/";
+ }
+ }
+ cookie.setPath(path);
+ // Android-added: b/25763487 A fix to verify cookie URI before removal
+ } else {
+ // Validate existing path
+ if (!pathMatches(uri, cookie)) {
+ continue;
+ }
+ }
+
+ // As per RFC 2965, section 3.3.1:
+ // Domain Defaults to the effective request-host. (Note that because
+ // there is no dot at the beginning of effective request-host,
+ // the default Domain can only domain-match itself.)
+ if (cookie.getDomain() == null) {
+ String host = uri.getHost();
+ if (host != null && !host.contains("."))
+ host += ".local";
+ cookie.setDomain(host);
+ }
+ String ports = cookie.getPortlist();
+ if (ports != null) {
+ int port = uri.getPort();
+ if (port == -1) {
+ port = "https".equals(uri.getScheme()) ? 443 : 80;
+ }
+ if (ports.isEmpty()) {
+ // Empty port list means this should be restricted
+ // to the incoming URI port
+ cookie.setPortlist("" + port );
+ if (shouldAcceptInternal(uri, cookie)) {
+ cookieJar.add(uri, cookie);
+ }
+ } else {
+ // Only store cookies with a port list
+ // IF the URI port is in that list, as per
+ // RFC 2965 section 3.3.2
+ if (isInPortList(ports, port) &&
+ shouldAcceptInternal(uri, cookie)) {
+ cookieJar.add(uri, cookie);
+ }
+ }
+ } else {
+ if (shouldAcceptInternal(uri, cookie)) {
+ cookieJar.add(uri, cookie);
+ }
+ }
+ }
+ } catch (IllegalArgumentException e) {
+ // invalid set-cookie header string
+ // no-op
+ }
+ }
+ }
+ }
+
+
+ /* ---------------- Private operations -------------- */
+
+ // to determine whether or not accept this cookie
+ private boolean shouldAcceptInternal(URI uri, HttpCookie cookie) {
+ try {
+ return policyCallback.shouldAccept(uri, cookie);
+ } catch (Exception ignored) { // pretect against malicious callback
+ return false;
+ }
+ }
+
+
+ static private boolean isInPortList(String lst, int port) {
+ int i = lst.indexOf(",");
+ int val = -1;
+ while (i > 0) {
+ try {
+ val = Integer.parseInt(lst.substring(0, i));
+ if (val == port) {
+ return true;
+ }
+ } catch (NumberFormatException numberFormatException) {
+ }
+ lst = lst.substring(i+1);
+ i = lst.indexOf(",");
+ }
+ if (!lst.isEmpty()) {
+ try {
+ val = Integer.parseInt(lst);
+ if (val == port) {
+ return true;
+ }
+ } catch (NumberFormatException numberFormatException) {
+ }
+ }
+ return false;
+ }
+
+ // Android-changed: b/25763487 Cookie path matching logic in OpenJDK was wrong
+ /**
+ * Return true iff. the path from {@code cookie} matches the path from {@code uri}.
+ */
+ private static boolean pathMatches(URI uri, HttpCookie cookie) {
+ return normalizePath(uri.getPath()).startsWith(normalizePath(cookie.getPath()));
+ }
+
+ private static String normalizePath(String path) {
+ if (path == null) {
+ path = "";
+ }
+
+ if (!path.endsWith("/")) {
+ path = path + "/";
+ }
+
+ return path;
+ }
+
+
+ /*
+ * sort cookies with respect to their path: those with more specific Path attributes
+ * precede those with less specific, as defined in RFC 2965 sec. 3.3.4
+ */
+ private List<String> sortByPath(List<HttpCookie> cookies) {
+ Collections.sort(cookies, new CookiePathComparator());
+
+ // BEGIN Android-changed: Netscape cookie spec and RFC 2965 have different format
+ // of Cookie header; RFC 2965 requires a leading $Version="1" string while Netscape does not
+ // The workaround here is to add a $Version="1" string in advance
+ final StringBuilder result = new StringBuilder();
+ int minVersion = 1;
+ for (HttpCookie cookie : cookies) {
+ if (cookie.getVersion() < minVersion) {
+ minVersion = cookie.getVersion();
+ }
+ }
+
+ if (minVersion == 1) {
+ result.append("$Version=\"1\"; ");
+ }
+
+ for (int i = 0; i < cookies.size(); ++i) {
+ if (i != 0) {
+ result.append("; ");
+ }
+
+ result.append(cookies.get(i).toString());
+ }
+
+ List<String> cookieHeader = new java.util.ArrayList<String>();
+ cookieHeader.add(result.toString());
+ // END Android-changed: Netscape cookie spec and RFC 2965 have different format
+ return cookieHeader;
+ }
+
+
+ static class CookiePathComparator implements Comparator<HttpCookie> {
+ public int compare(HttpCookie c1, HttpCookie c2) {
+ if (c1 == c2) return 0;
+ if (c1 == null) return -1;
+ if (c2 == null) return 1;
+
+ // path rule only applies to the cookies with same name
+ if (!c1.getName().equals(c2.getName())) return 0;
+
+ // Android-changed: normalize before comparison
+ final String c1Path = normalizePath(c1.getPath());
+ final String c2Path = normalizePath(c2.getPath());
+
+ // those with more specific Path attributes precede those with less specific
+ if (c1Path.startsWith(c2Path))
+ return -1;
+ else if (c2Path.startsWith(c1Path))
+ return 1;
+ else
+ return 0;
+ }
+ }
+}
diff --git a/java/net/CookiePolicy.java b/java/net/CookiePolicy.java
new file mode 100644
index 0000000..8094835
--- /dev/null
+++ b/java/net/CookiePolicy.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * CookiePolicy implementations decide which cookies should be accepted
+ * and which should be rejected. Three pre-defined policy implementations
+ * are provided, namely ACCEPT_ALL, ACCEPT_NONE and ACCEPT_ORIGINAL_SERVER.
+ *
+ * <p>See RFC 2965 sec. 3.3 and 7 for more detail.
+ *
+ * @author Edward Wang
+ * @since 1.6
+ */
+public interface CookiePolicy {
+ /**
+ * One pre-defined policy which accepts all cookies.
+ */
+ public static final CookiePolicy ACCEPT_ALL = new CookiePolicy(){
+ public boolean shouldAccept(URI uri, HttpCookie cookie) {
+ return true;
+ }
+ };
+
+ /**
+ * One pre-defined policy which accepts no cookies.
+ */
+ public static final CookiePolicy ACCEPT_NONE = new CookiePolicy(){
+ public boolean shouldAccept(URI uri, HttpCookie cookie) {
+ return false;
+ }
+ };
+
+ /**
+ * One pre-defined policy which only accepts cookies from original server.
+ */
+ public static final CookiePolicy ACCEPT_ORIGINAL_SERVER = new CookiePolicy(){
+ public boolean shouldAccept(URI uri, HttpCookie cookie) {
+ if (uri == null || cookie == null)
+ return false;
+ return HttpCookie.domainMatches(cookie.getDomain(), uri.getHost());
+ }
+ };
+
+
+ /**
+ * Will be called to see whether or not this cookie should be accepted.
+ *
+ * @param uri the URI to consult accept policy with
+ * @param cookie the HttpCookie object in question
+ * @return {@code true} if this cookie should be accepted;
+ * otherwise, {@code false}
+ */
+ public boolean shouldAccept(URI uri, HttpCookie cookie);
+}
diff --git a/java/net/CookieStore.java b/java/net/CookieStore.java
new file mode 100644
index 0000000..105cc66
--- /dev/null
+++ b/java/net/CookieStore.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A CookieStore object represents a storage for cookie. Can store and retrieve
+ * cookies.
+ *
+ * <p>{@link CookieManager} will call {@code CookieStore.add} to save cookies
+ * for every incoming HTTP response, and call {@code CookieStore.get} to
+ * retrieve cookie for every outgoing HTTP request. A CookieStore
+ * is responsible for removing HttpCookie instances which have expired.
+ *
+ * @author Edward Wang
+ * @since 1.6
+ */
+public interface CookieStore {
+ /**
+ * Adds one HTTP cookie to the store. This is called for every
+ * incoming HTTP response.
+ *
+ * <p>A cookie to store may or may not be associated with an URI. If it
+ * is not associated with an URI, the cookie's domain and path attribute
+ * will indicate where it comes from. If it is associated with an URI and
+ * its domain and path attribute are not specified, given URI will indicate
+ * where this cookie comes from.
+ *
+ * <p>If a cookie corresponding to the given URI already exists,
+ * then it is replaced with the new one.
+ *
+ * @param uri the uri this cookie associated with.
+ * if {@code null}, this cookie will not be associated
+ * with an URI
+ * @param cookie the cookie to store
+ *
+ * @throws NullPointerException if {@code cookie} is {@code null}
+ *
+ * @see #get
+ *
+ */
+ public void add(URI uri, HttpCookie cookie);
+
+
+ /**
+ * Retrieve cookies associated with given URI, or whose domain matches the
+ * given URI. Only cookies that have not expired are returned.
+ * This is called for every outgoing HTTP request.
+ *
+ * @return an immutable list of HttpCookie,
+ * return empty list if no cookies match the given URI
+ *
+ * @param uri the uri associated with the cookies to be returned
+ *
+ * @throws NullPointerException if {@code uri} is {@code null}
+ *
+ * @see #add
+ *
+ */
+ public List<HttpCookie> get(URI uri);
+
+
+ /**
+ * Get all not-expired cookies in cookie store.
+ *
+ * @return an immutable list of http cookies;
+ * return empty list if there's no http cookie in store
+ */
+ public List<HttpCookie> getCookies();
+
+
+ /**
+ * Get all URIs which identify the cookies in this cookie store.
+ *
+ * @return an immutable list of URIs;
+ * return empty list if no cookie in this cookie store
+ * is associated with an URI
+ */
+ public List<URI> getURIs();
+
+
+ /**
+ * Remove a cookie from store.
+ *
+ * @param uri the uri this cookie associated with.
+ * if {@code null}, the cookie to be removed is not associated
+ * with an URI when added; if not {@code null}, the cookie
+ * to be removed is associated with the given URI when added.
+ * @param cookie the cookie to remove
+ *
+ * @return {@code true} if this store contained the specified cookie
+ *
+ * @throws NullPointerException if {@code cookie} is {@code null}
+ */
+ public boolean remove(URI uri, HttpCookie cookie);
+
+
+ /**
+ * Remove all cookies in this cookie store.
+ *
+ * @return {@code true} if this store changed as a result of the call
+ */
+ public boolean removeAll();
+}
diff --git a/java/net/DatagramPacket.java b/java/net/DatagramPacket.java
new file mode 100644
index 0000000..c1bd692
--- /dev/null
+++ b/java/net/DatagramPacket.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * This class represents a datagram packet.
+ * <p>
+ * Datagram packets are used to implement a connectionless packet
+ * delivery service. Each message is routed from one machine to
+ * another based solely on information contained within that packet.
+ * Multiple packets sent from one machine to another might be routed
+ * differently, and might arrive in any order. Packet delivery is
+ * not guaranteed.
+ *
+ * @author Pavani Diwanji
+ * @author Benjamin Renaud
+ * @since JDK1.0
+ */
+public final
+class DatagramPacket {
+
+ // BEGIN Android-removed: Android doesn't need to load native net library.
+ /**
+ * Perform class initialization
+ *
+ static {
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("net");
+ return null;
+ }
+ });
+ init();
+ }
+ */
+ // END Android-removed: Android doesn't need to load native net library.
+
+ /*
+ * The fields of this class are package-private since DatagramSocketImpl
+ * classes needs to access them.
+ */
+ byte[] buf;
+ int offset;
+ int length;
+ int bufLength;
+ InetAddress address;
+ int port;
+
+ /**
+ * Constructs a {@code DatagramPacket} for receiving packets of
+ * length {@code length}, specifying an offset into the buffer.
+ * <p>
+ * The {@code length} argument must be less than or equal to
+ * {@code buf.length}.
+ *
+ * @param buf buffer for holding the incoming datagram.
+ * @param offset the offset for the buffer
+ * @param length the number of bytes to read.
+ *
+ * @since 1.2
+ */
+ public DatagramPacket(byte buf[], int offset, int length) {
+ setData(buf, offset, length);
+ this.address = null;
+ this.port = -1;
+ }
+
+ /**
+ * Constructs a {@code DatagramPacket} for receiving packets of
+ * length {@code length}.
+ * <p>
+ * The {@code length} argument must be less than or equal to
+ * {@code buf.length}.
+ *
+ * @param buf buffer for holding the incoming datagram.
+ * @param length the number of bytes to read.
+ */
+ public DatagramPacket(byte buf[], int length) {
+ this (buf, 0, length);
+ }
+
+ /**
+ * Constructs a datagram packet for sending packets of length
+ * {@code length} with offset {@code ioffset}to the
+ * specified port number on the specified host. The
+ * {@code length} argument must be less than or equal to
+ * {@code buf.length}.
+ *
+ * @param buf the packet data.
+ * @param offset the packet data offset.
+ * @param length the packet data length.
+ * @param address the destination address.
+ * @param port the destination port number.
+ * @see java.net.InetAddress
+ *
+ * @since 1.2
+ */
+ public DatagramPacket(byte buf[], int offset, int length,
+ InetAddress address, int port) {
+ setData(buf, offset, length);
+ setAddress(address);
+ setPort(port);
+ }
+
+ // Android-changed: Added Android-specific notes regarding the exception signature change.
+ /**
+ * Constructs a datagram packet for sending packets of length
+ * {@code length} with offset {@code ioffset}to the
+ * specified port number on the specified host. The
+ * {@code length} argument must be less than or equal to
+ * {@code buf.length}.
+ *
+ * <p>
+ * <em>Android note</em>: Up to and including API 25 this method declared that a SocketException
+ * can be thrown, although the exception is never thrown. Code compiled against a newer SDK does
+ * not need to catch the exception and will be binary compatible with older versions of Android.
+ *
+ * @param buf the packet data.
+ * @param offset the packet data offset.
+ * @param length the packet data length.
+ * @param address the destination socket address.
+ * @throws IllegalArgumentException if address type is not supported
+ * @see java.net.InetAddress
+ *
+ * @since 1.4
+ */
+ public DatagramPacket(byte buf[], int offset, int length, SocketAddress address) {
+ setData(buf, offset, length);
+ setSocketAddress(address);
+ }
+
+ // Android-changed: Added Android-specific notes regarding the exception signature change.
+ /**
+ * Constructs a datagram packet for sending packets of length
+ * {@code length} to the specified port number on the specified
+ * host. The {@code length} argument must be less than or equal
+ * to {@code buf.length}.
+ *
+ * <p>
+ * <em>Android note</em>: Up to and including API 25 this method declared that a SocketException
+ * can be thrown, although the exception is never thrown. Code compiled against a newer SDK does
+ * not need to catch the exception and will be binary compatible with older versions of Android.
+ *
+ * @param buf the packet data.
+ * @param length the packet length.
+ * @param address the destination address.
+ * @param port the destination port number.
+ * @see java.net.InetAddress
+ */
+ public DatagramPacket(byte buf[], int length,
+ InetAddress address, int port) {
+ this(buf, 0, length, address, port);
+ }
+
+ /**
+ * Constructs a datagram packet for sending packets of length
+ * {@code length} to the specified port number on the specified
+ * host. The {@code length} argument must be less than or equal
+ * to {@code buf.length}.
+ *
+ * @param buf the packet data.
+ * @param length the packet length.
+ * @param address the destination address.
+ * @throws IllegalArgumentException if address type is not supported
+ * @since 1.4
+ * @see java.net.InetAddress
+ */
+ public DatagramPacket(byte buf[], int length, SocketAddress address) {
+ this(buf, 0, length, address);
+ }
+
+ /**
+ * Returns the IP address of the machine to which this datagram is being
+ * sent or from which the datagram was received.
+ *
+ * @return the IP address of the machine to which this datagram is being
+ * sent or from which the datagram was received.
+ * @see java.net.InetAddress
+ * @see #setAddress(java.net.InetAddress)
+ */
+ public synchronized InetAddress getAddress() {
+ return address;
+ }
+
+ /**
+ * Returns the port number on the remote host to which this datagram is
+ * being sent or from which the datagram was received.
+ *
+ * @return the port number on the remote host to which this datagram is
+ * being sent or from which the datagram was received.
+ * @see #setPort(int)
+ */
+ public synchronized int getPort() {
+ return port;
+ }
+
+ /**
+ * Returns the data buffer. The data received or the data to be sent
+ * starts from the {@code offset} in the buffer,
+ * and runs for {@code length} long.
+ *
+ * @return the buffer used to receive or send data
+ * @see #setData(byte[], int, int)
+ */
+ public synchronized byte[] getData() {
+ return buf;
+ }
+
+ /**
+ * Returns the offset of the data to be sent or the offset of the
+ * data received.
+ *
+ * @return the offset of the data to be sent or the offset of the
+ * data received.
+ *
+ * @since 1.2
+ */
+ public synchronized int getOffset() {
+ return offset;
+ }
+
+ /**
+ * Returns the length of the data to be sent or the length of the
+ * data received.
+ *
+ * @return the length of the data to be sent or the length of the
+ * data received.
+ * @see #setLength(int)
+ */
+ public synchronized int getLength() {
+ return length;
+ }
+
+ /**
+ * Set the data buffer for this packet. This sets the
+ * data, length and offset of the packet.
+ *
+ * @param buf the buffer to set for this packet
+ *
+ * @param offset the offset into the data
+ *
+ * @param length the length of the data
+ * and/or the length of the buffer used to receive data
+ *
+ * @exception NullPointerException if the argument is null
+ *
+ * @see #getData
+ * @see #getOffset
+ * @see #getLength
+ *
+ * @since 1.2
+ */
+ public synchronized void setData(byte[] buf, int offset, int length) {
+ /* this will check to see if buf is null */
+ if (length < 0 || offset < 0 ||
+ (length + offset) < 0 ||
+ ((length + offset) > buf.length)) {
+ throw new IllegalArgumentException("illegal length or offset");
+ }
+ this.buf = buf;
+ this.length = length;
+ this.bufLength = length;
+ this.offset = offset;
+ }
+
+ /**
+ * Sets the IP address of the machine to which this datagram
+ * is being sent.
+ * @param iaddr the {@code InetAddress}
+ * @since JDK1.1
+ * @see #getAddress()
+ */
+ public synchronized void setAddress(InetAddress iaddr) {
+ address = iaddr;
+ }
+
+ // BEGIN Android-changed
+ /**
+ * Sets 'length' without changing 'userSuppliedLength', after receiving a packet.
+ * @hide for IoBridge
+ */
+ public void setReceivedLength(int length) {
+ this.length = length;
+ }
+ // END Android-changed
+
+ /**
+ * Sets the port number on the remote host to which this datagram
+ * is being sent.
+ * @param iport the port number
+ * @since JDK1.1
+ * @see #getPort()
+ */
+ public synchronized void setPort(int iport) {
+ if (iport < 0 || iport > 0xFFFF) {
+ throw new IllegalArgumentException("Port out of range:"+ iport);
+ }
+ port = iport;
+ }
+
+ /**
+ * Sets the SocketAddress (usually IP address + port number) of the remote
+ * host to which this datagram is being sent.
+ *
+ * @param address the {@code SocketAddress}
+ * @throws IllegalArgumentException if address is null or is a
+ * SocketAddress subclass not supported by this socket
+ *
+ * @since 1.4
+ * @see #getSocketAddress
+ */
+ public synchronized void setSocketAddress(SocketAddress address) {
+ if (address == null || !(address instanceof InetSocketAddress))
+ throw new IllegalArgumentException("unsupported address type");
+ InetSocketAddress addr = (InetSocketAddress) address;
+ if (addr.isUnresolved())
+ throw new IllegalArgumentException("unresolved address");
+ setAddress(addr.getAddress());
+ setPort(addr.getPort());
+ }
+
+ /**
+ * Gets the SocketAddress (usually IP address + port number) of the remote
+ * host that this packet is being sent to or is coming from.
+ *
+ * @return the {@code SocketAddress}
+ * @since 1.4
+ * @see #setSocketAddress
+ */
+ public synchronized SocketAddress getSocketAddress() {
+ return new InetSocketAddress(getAddress(), getPort());
+ }
+
+ /**
+ * Set the data buffer for this packet. With the offset of
+ * this DatagramPacket set to 0, and the length set to
+ * the length of {@code buf}.
+ *
+ * @param buf the buffer to set for this packet.
+ *
+ * @exception NullPointerException if the argument is null.
+ *
+ * @see #getLength
+ * @see #getData
+ *
+ * @since JDK1.1
+ */
+ public synchronized void setData(byte[] buf) {
+ if (buf == null) {
+ throw new NullPointerException("null packet buffer");
+ }
+ this.buf = buf;
+ this.offset = 0;
+ this.length = buf.length;
+ this.bufLength = buf.length;
+ }
+
+ /**
+ * Set the length for this packet. The length of the packet is
+ * the number of bytes from the packet's data buffer that will be
+ * sent, or the number of bytes of the packet's data buffer that
+ * will be used for receiving data. The length must be lesser or
+ * equal to the offset plus the length of the packet's buffer.
+ *
+ * @param length the length to set for this packet.
+ *
+ * @exception IllegalArgumentException if the length is negative
+ * of if the length is greater than the packet's data buffer
+ * length.
+ *
+ * @see #getLength
+ * @see #setData
+ *
+ * @since JDK1.1
+ */
+ public synchronized void setLength(int length) {
+ if ((length + offset) > buf.length || length < 0 ||
+ (length + offset) < 0) {
+ throw new IllegalArgumentException("illegal length");
+ }
+ this.length = length;
+ this.bufLength = this.length;
+ }
+
+ // Android-removed: JNI has been removed
+ // /**
+ // * Perform class load-time initializations.
+ // */
+ // private native static void init();
+}
diff --git a/java/net/DatagramSocket.annotated.java b/java/net/DatagramSocket.annotated.java
new file mode 100644
index 0000000..b462769
--- /dev/null
+++ b/java/net/DatagramSocket.annotated.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package java.net;
+
+import java.nio.channels.DatagramChannel;
+import java.io.IOException;
+
+@SuppressWarnings({"unchecked", "deprecation", "all"})
+public class DatagramSocket implements java.io.Closeable {
+
+public DatagramSocket() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+protected DatagramSocket(java.net.DatagramSocketImpl impl) { throw new RuntimeException("Stub!"); }
+
+public DatagramSocket(java.net.SocketAddress bindaddr) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public DatagramSocket(int port) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public DatagramSocket(int port, java.net.InetAddress laddr) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized void bind(java.net.SocketAddress addr) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public void connect(java.net.InetAddress address, int port) { throw new RuntimeException("Stub!"); }
+
+public void connect(java.net.SocketAddress addr) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public void disconnect() { throw new RuntimeException("Stub!"); }
+
+public boolean isBound() { throw new RuntimeException("Stub!"); }
+
+public boolean isConnected() { throw new RuntimeException("Stub!"); }
+
+public java.net.InetAddress getInetAddress() { throw new RuntimeException("Stub!"); }
+
+public int getPort() { throw new RuntimeException("Stub!"); }
+
+public java.net.SocketAddress getRemoteSocketAddress() { throw new RuntimeException("Stub!"); }
+
+public java.net.SocketAddress getLocalSocketAddress() { throw new RuntimeException("Stub!"); }
+
+public void send(java.net.DatagramPacket p) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public synchronized void receive(java.net.DatagramPacket p) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public java.net.InetAddress getLocalAddress() { throw new RuntimeException("Stub!"); }
+
+public int getLocalPort() { throw new RuntimeException("Stub!"); }
+
+public synchronized void setSoTimeout(int timeout) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized int getSoTimeout() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized void setSendBufferSize(int size) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized int getSendBufferSize() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized void setReceiveBufferSize(int size) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized int getReceiveBufferSize() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized void setReuseAddress(boolean on) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized boolean getReuseAddress() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized void setBroadcast(boolean on) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized boolean getBroadcast() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized void setTrafficClass(int tc) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized int getTrafficClass() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public void close() { throw new RuntimeException("Stub!"); }
+
+public boolean isClosed() { throw new RuntimeException("Stub!"); }
+
+public java.nio.channels.DatagramChannel getChannel() { throw new RuntimeException("Stub!"); }
+
+public static synchronized void setDatagramSocketImplFactory(java.net.DatagramSocketImplFactory fac) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
[email protected]
+public java.io.FileDescriptor getFileDescriptor$() { throw new RuntimeException("Stub!"); }
+}
+
diff --git a/java/net/DatagramSocket.java b/java/net/DatagramSocket.java
new file mode 100644
index 0000000..3e3faf7
--- /dev/null
+++ b/java/net/DatagramSocket.java
@@ -0,0 +1,1358 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_BINDTODEVICE;
+
+import android.system.ErrnoException;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.nio.channels.DatagramChannel;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+import libcore.io.Libcore;
+
+/**
+ * This class represents a socket for sending and receiving datagram packets.
+ *
+ * <p>A datagram socket is the sending or receiving point for a packet
+ * delivery service. Each packet sent or received on a datagram socket
+ * is individually addressed and routed. Multiple packets sent from
+ * one machine to another may be routed differently, and may arrive in
+ * any order.
+ *
+ * <p> Where possible, a newly constructed {@code DatagramSocket} has the
+ * {@link SocketOptions#SO_BROADCAST SO_BROADCAST} socket option enabled so as
+ * to allow the transmission of broadcast datagrams. In order to receive
+ * broadcast packets a DatagramSocket should be bound to the wildcard address.
+ * In some implementations, broadcast packets may also be received when
+ * a DatagramSocket is bound to a more specific address.
+ * <p>
+ * Example:
+ * {@code
+ * DatagramSocket s = new DatagramSocket(null);
+ * s.bind(new InetSocketAddress(8888));
+ * }
+ * Which is equivalent to:
+ * {@code
+ * DatagramSocket s = new DatagramSocket(8888);
+ * }
+ * Both cases will create a DatagramSocket able to receive broadcasts on
+ * UDP port 8888.
+ *
+ * @author Pavani Diwanji
+ * @see java.net.DatagramPacket
+ * @see java.nio.channels.DatagramChannel
+ * @since JDK1.0
+ */
+public
+class DatagramSocket implements java.io.Closeable {
+ /**
+ * Various states of this socket.
+ */
+ private boolean created = false;
+ private boolean bound = false;
+ private boolean closed = false;
+ private Object closeLock = new Object();
+
+ /*
+ * The implementation of this DatagramSocket.
+ */
+ DatagramSocketImpl impl;
+
+ /**
+ * Are we using an older DatagramSocketImpl?
+ */
+ boolean oldImpl = false;
+
+ /**
+ * Set when a socket is ST_CONNECTED until we are certain
+ * that any packets which might have been received prior
+ * to calling connect() but not read by the application
+ * have been read. During this time we check the source
+ * address of all packets received to be sure they are from
+ * the connected destination. Other packets are read but
+ * silently dropped.
+ */
+ private boolean explicitFilter = false;
+ private int bytesLeftToFilter;
+ /*
+ * Connection state:
+ * ST_NOT_CONNECTED = socket not connected
+ * ST_CONNECTED = socket connected
+ * ST_CONNECTED_NO_IMPL = socket connected but not at impl level
+ */
+ static final int ST_NOT_CONNECTED = 0;
+ static final int ST_CONNECTED = 1;
+ static final int ST_CONNECTED_NO_IMPL = 2;
+
+ int connectState = ST_NOT_CONNECTED;
+
+ /*
+ * Connected address & port
+ */
+ InetAddress connectedAddress = null;
+ int connectedPort = -1;
+
+ // Android-added: Store pending exception from connect
+ private SocketException pendingConnectException;
+
+ /**
+ * Connects this socket to a remote socket address (IP address + port number).
+ * Binds socket if not already bound.
+ * <p>
+ * @param address The remote address.
+ * @param port The remote port
+ * @throws SocketException if binding the socket fails.
+ */
+ private synchronized void connectInternal(InetAddress address, int port) throws SocketException {
+ if (port < 0 || port > 0xFFFF) {
+ throw new IllegalArgumentException("connect: " + port);
+ }
+ if (address == null) {
+ throw new IllegalArgumentException("connect: null address");
+ }
+ checkAddress (address, "connect");
+ if (isClosed())
+ return;
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ if (address.isMulticastAddress()) {
+ security.checkMulticast(address);
+ } else {
+ security.checkConnect(address.getHostAddress(), port);
+ security.checkAccept(address.getHostAddress(), port);
+ }
+ }
+
+ if (!isBound())
+ bind(new InetSocketAddress(0));
+
+ // Android-changed: This section now throws any SocketException generated by connect()
+ // to enable it to be recorded as the pendingConnectException. It has been enclosed in a
+ // try-finally to ensure connectedAddress and connectedPort are set when the exception
+ // is thrown.
+ try {
+ // old impls do not support connect/disconnect
+ // Android-changed: Added special handling for AbstractPlainDatagramSocketImpl in
+ // the condition below.
+ if (oldImpl || (impl instanceof AbstractPlainDatagramSocketImpl &&
+ ((AbstractPlainDatagramSocketImpl)impl).nativeConnectDisabled())) {
+ connectState = ST_CONNECTED_NO_IMPL;
+ } else {
+ try {
+ getImpl().connect(address, port);
+
+ // socket is now connected by the impl
+ connectState = ST_CONNECTED;
+
+ // Do we need to filter some packets?
+ int avail = getImpl().dataAvailable();
+ if (avail == -1) {
+ throw new SocketException();
+ }
+ explicitFilter = avail > 0;
+ if (explicitFilter) {
+ bytesLeftToFilter = getReceiveBufferSize();
+ }
+ } catch (SocketException se) {
+ // connection will be emulated by DatagramSocket
+ connectState = ST_CONNECTED_NO_IMPL;
+ // Android-changed: Propagate the SocketException so connect() can store it.
+ throw se;
+ }
+ }
+ } finally {
+ connectedAddress = address;
+ connectedPort = port;
+ }
+ }
+
+
+ /**
+ * Constructs a datagram socket and binds it to any available port
+ * on the local host machine. The socket will be bound to the
+ * {@link InetAddress#isAnyLocalAddress wildcard} address,
+ * an IP address chosen by the kernel.
+ *
+ * <p>If there is a security manager,
+ * its {@code checkListen} method is first called
+ * with 0 as its argument to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ *
+ * @exception SocketException if the socket could not be opened,
+ * or the socket could not bind to the specified local port.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkListen} method doesn't allow the operation.
+ *
+ * @see SecurityManager#checkListen
+ */
+ public DatagramSocket() throws SocketException {
+ this(new InetSocketAddress(0));
+ }
+
+ /**
+ * Creates an unbound datagram socket with the specified
+ * DatagramSocketImpl.
+ *
+ * @param impl an instance of a <B>DatagramSocketImpl</B>
+ * the subclass wishes to use on the DatagramSocket.
+ * @since 1.4
+ */
+ protected DatagramSocket(DatagramSocketImpl impl) {
+ if (impl == null)
+ throw new NullPointerException();
+ this.impl = impl;
+ checkOldImpl();
+ }
+
+ /**
+ * Creates a datagram socket, bound to the specified local
+ * socket address.
+ * <p>
+ * If, if the address is {@code null}, creates an unbound socket.
+ *
+ * <p>If there is a security manager,
+ * its {@code checkListen} method is first called
+ * with the port from the socket address
+ * as its argument to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ *
+ * @param bindaddr local socket address to bind, or {@code null}
+ * for an unbound socket.
+ *
+ * @exception SocketException if the socket could not be opened,
+ * or the socket could not bind to the specified local port.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkListen} method doesn't allow the operation.
+ *
+ * @see SecurityManager#checkListen
+ * @since 1.4
+ */
+ public DatagramSocket(SocketAddress bindaddr) throws SocketException {
+ // create a datagram socket.
+ createImpl();
+ if (bindaddr != null) {
+ try {
+ bind(bindaddr);
+ } finally {
+ if (!isBound())
+ close();
+ }
+ }
+ }
+
+ /**
+ * Constructs a datagram socket and binds it to the specified port
+ * on the local host machine. The socket will be bound to the
+ * {@link InetAddress#isAnyLocalAddress wildcard} address,
+ * an IP address chosen by the kernel.
+ *
+ * <p>If there is a security manager,
+ * its {@code checkListen} method is first called
+ * with the {@code port} argument
+ * as its argument to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ *
+ * @param port port to use.
+ * @exception SocketException if the socket could not be opened,
+ * or the socket could not bind to the specified local port.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkListen} method doesn't allow the operation.
+ *
+ * @see SecurityManager#checkListen
+ */
+ public DatagramSocket(int port) throws SocketException {
+ this(port, null);
+ }
+
+ /**
+ * Creates a datagram socket, bound to the specified local
+ * address. The local port must be between 0 and 65535 inclusive.
+ * If the IP address is 0.0.0.0, the socket will be bound to the
+ * {@link InetAddress#isAnyLocalAddress wildcard} address,
+ * an IP address chosen by the kernel.
+ *
+ * <p>If there is a security manager,
+ * its {@code checkListen} method is first called
+ * with the {@code port} argument
+ * as its argument to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ *
+ * @param port local port to use
+ * @param laddr local address to bind
+ *
+ * @exception SocketException if the socket could not be opened,
+ * or the socket could not bind to the specified local port.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkListen} method doesn't allow the operation.
+ *
+ * @see SecurityManager#checkListen
+ * @since JDK1.1
+ */
+ public DatagramSocket(int port, InetAddress laddr) throws SocketException {
+ this(new InetSocketAddress(laddr, port));
+ }
+
+ private void checkOldImpl() {
+ if (impl == null)
+ return;
+ // DatagramSocketImpl.peekdata() is a protected method, therefore we need to use
+ // getDeclaredMethod, therefore we need permission to access the member
+ try {
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Void>() {
+ public Void run() throws NoSuchMethodException {
+ Class<?>[] cl = new Class<?>[1];
+ cl[0] = DatagramPacket.class;
+ impl.getClass().getDeclaredMethod("peekData", cl);
+ return null;
+ }
+ });
+ } catch (java.security.PrivilegedActionException e) {
+ oldImpl = true;
+ }
+ }
+
+ static Class<?> implClass = null;
+
+ void createImpl() throws SocketException {
+ if (impl == null) {
+ if (factory != null) {
+ impl = factory.createDatagramSocketImpl();
+ checkOldImpl();
+ } else {
+ boolean isMulticast = (this instanceof MulticastSocket) ? true : false;
+ impl = DefaultDatagramSocketImplFactory.createDatagramSocketImpl(isMulticast);
+
+ checkOldImpl();
+ }
+ }
+ // creates a udp socket
+ impl.create();
+ impl.setDatagramSocket(this);
+ created = true;
+ }
+
+ /**
+ * Get the {@code DatagramSocketImpl} attached to this socket,
+ * creating it if necessary.
+ *
+ * @return the {@code DatagramSocketImpl} attached to that
+ * DatagramSocket
+ * @throws SocketException if creation fails.
+ * @since 1.4
+ */
+ DatagramSocketImpl getImpl() throws SocketException {
+ if (!created)
+ createImpl();
+ return impl;
+ }
+
+ /**
+ * Binds this DatagramSocket to a specific address and port.
+ * <p>
+ * If the address is {@code null}, then the system will pick up
+ * an ephemeral port and a valid local address to bind the socket.
+ *<p>
+ * @param addr The address and port to bind to.
+ * @throws SocketException if any error happens during the bind, or if the
+ * socket is already bound.
+ * @throws SecurityException if a security manager exists and its
+ * {@code checkListen} method doesn't allow the operation.
+ * @throws IllegalArgumentException if addr is a SocketAddress subclass
+ * not supported by this socket.
+ * @since 1.4
+ */
+ public synchronized void bind(SocketAddress addr) throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ if (isBound())
+ throw new SocketException("already bound");
+ if (addr == null)
+ addr = new InetSocketAddress(0);
+ if (!(addr instanceof InetSocketAddress))
+ throw new IllegalArgumentException("Unsupported address type!");
+ InetSocketAddress epoint = (InetSocketAddress) addr;
+ if (epoint.isUnresolved())
+ throw new SocketException("Unresolved address");
+ InetAddress iaddr = epoint.getAddress();
+ int port = epoint.getPort();
+ checkAddress(iaddr, "bind");
+ SecurityManager sec = System.getSecurityManager();
+ if (sec != null) {
+ sec.checkListen(port);
+ }
+ try {
+ getImpl().bind(port, iaddr);
+ } catch (SocketException e) {
+ getImpl().close();
+ throw e;
+ }
+ bound = true;
+ }
+
+ void checkAddress (InetAddress addr, String op) {
+ if (addr == null) {
+ return;
+ }
+ if (!(addr instanceof Inet4Address || addr instanceof Inet6Address)) {
+ throw new IllegalArgumentException(op + ": invalid address type");
+ }
+ }
+
+ /**
+ * Connects the socket to a remote address for this socket. When a
+ * socket is connected to a remote address, packets may only be
+ * sent to or received from that address. By default a datagram
+ * socket is not connected.
+ *
+ * <p>If the remote destination to which the socket is connected does not
+ * exist, or is otherwise unreachable, and if an ICMP destination unreachable
+ * packet has been received for that address, then a subsequent call to
+ * send or receive may throw a PortUnreachableException. Note, there is no
+ * guarantee that the exception will be thrown.
+ *
+ * <p> If a security manager has been installed then it is invoked to check
+ * access to the remote address. Specifically, if the given {@code address}
+ * is a {@link InetAddress#isMulticastAddress multicast address},
+ * the security manager's {@link
+ * java.lang.SecurityManager#checkMulticast(InetAddress)
+ * checkMulticast} method is invoked with the given {@code address}.
+ * Otherwise, the security manager's {@link
+ * java.lang.SecurityManager#checkConnect(String,int) checkConnect}
+ * and {@link java.lang.SecurityManager#checkAccept checkAccept} methods
+ * are invoked, with the given {@code address} and {@code port}, to
+ * verify that datagrams are permitted to be sent and received
+ * respectively.
+ *
+ * <p> When a socket is connected, {@link #receive receive} and
+ * {@link #send send} <b>will not perform any security checks</b>
+ * on incoming and outgoing packets, other than matching the packet's
+ * and the socket's address and port. On a send operation, if the
+ * packet's address is set and the packet's address and the socket's
+ * address do not match, an {@code IllegalArgumentException} will be
+ * thrown. A socket connected to a multicast address may only be used
+ * to send packets.
+ *
+ * @param address the remote address for the socket
+ *
+ * @param port the remote port for the socket.
+ *
+ * @throws IllegalArgumentException
+ * if the address is null, or the port is out of range.
+ *
+ * @throws SecurityException
+ * if a security manager has been installed and it does
+ * not permit access to the given remote address
+ *
+ * @see #disconnect
+ */
+ public void connect(InetAddress address, int port) {
+ try {
+ connectInternal(address, port);
+ } catch (SocketException se) {
+ // Android-changed: this method can't throw checked SocketException. Throw it later
+ // throw new Error("connect failed", se);
+ // TODO: or just use SneakyThrow? There's a clear API bug here.
+ pendingConnectException = se;
+ }
+ }
+
+ /**
+ * Connects this socket to a remote socket address (IP address + port number).
+ *
+ * <p> If given an {@link InetSocketAddress InetSocketAddress}, this method
+ * behaves as if invoking {@link #connect(InetAddress,int) connect(InetAddress,int)}
+ * with the the given socket addresses IP address and port number.
+ *
+ * @param addr The remote address.
+ *
+ * @throws SocketException
+ * if the connect fails
+ *
+ * @throws IllegalArgumentException
+ * if {@code addr} is {@code null}, or {@code addr} is a SocketAddress
+ * subclass not supported by this socket
+ *
+ * @throws SecurityException
+ * if a security manager has been installed and it does
+ * not permit access to the given remote address
+ *
+ * @since 1.4
+ */
+ public void connect(SocketAddress addr) throws SocketException {
+ if (addr == null)
+ throw new IllegalArgumentException("Address can't be null");
+ if (!(addr instanceof InetSocketAddress))
+ throw new IllegalArgumentException("Unsupported address type");
+ InetSocketAddress epoint = (InetSocketAddress) addr;
+ if (epoint.isUnresolved())
+ throw new SocketException("Unresolved address");
+ connectInternal(epoint.getAddress(), epoint.getPort());
+ }
+
+ /**
+ * Disconnects the socket. If the socket is closed or not connected,
+ * then this method has no effect.
+ *
+ * @see #connect
+ */
+ public void disconnect() {
+ synchronized (this) {
+ if (isClosed())
+ return;
+ if (connectState == ST_CONNECTED) {
+ impl.disconnect ();
+ }
+ connectedAddress = null;
+ connectedPort = -1;
+ connectState = ST_NOT_CONNECTED;
+ explicitFilter = false;
+ }
+ }
+
+ /**
+ * Returns the binding state of the socket.
+ * <p>
+ * If the socket was bound prior to being {@link #close closed},
+ * then this method will continue to return {@code true}
+ * after the socket is closed.
+ *
+ * @return true if the socket successfully bound to an address
+ * @since 1.4
+ */
+ public boolean isBound() {
+ return bound;
+ }
+
+ /**
+ * Returns the connection state of the socket.
+ * <p>
+ * If the socket was connected prior to being {@link #close closed},
+ * then this method will continue to return {@code true}
+ * after the socket is closed.
+ *
+ * @return true if the socket successfully connected to a server
+ * @since 1.4
+ */
+ public boolean isConnected() {
+ return connectState != ST_NOT_CONNECTED;
+ }
+
+ /**
+ * Returns the address to which this socket is connected. Returns
+ * {@code null} if the socket is not connected.
+ * <p>
+ * If the socket was connected prior to being {@link #close closed},
+ * then this method will continue to return the connected address
+ * after the socket is closed.
+ *
+ * @return the address to which this socket is connected.
+ */
+ public InetAddress getInetAddress() {
+ return connectedAddress;
+ }
+
+ /**
+ * Returns the port number to which this socket is connected.
+ * Returns {@code -1} if the socket is not connected.
+ * <p>
+ * If the socket was connected prior to being {@link #close closed},
+ * then this method will continue to return the connected port number
+ * after the socket is closed.
+ *
+ * @return the port number to which this socket is connected.
+ */
+ public int getPort() {
+ return connectedPort;
+ }
+
+ /**
+ * Returns the address of the endpoint this socket is connected to, or
+ * {@code null} if it is unconnected.
+ * <p>
+ * If the socket was connected prior to being {@link #close closed},
+ * then this method will continue to return the connected address
+ * after the socket is closed.
+ *
+ * @return a {@code SocketAddress} representing the remote
+ * endpoint of this socket, or {@code null} if it is
+ * not connected yet.
+ * @see #getInetAddress()
+ * @see #getPort()
+ * @see #connect(SocketAddress)
+ * @since 1.4
+ */
+ public SocketAddress getRemoteSocketAddress() {
+ if (!isConnected())
+ return null;
+ return new InetSocketAddress(getInetAddress(), getPort());
+ }
+
+ /**
+ * Returns the address of the endpoint this socket is bound to.
+ *
+ * @return a {@code SocketAddress} representing the local endpoint of this
+ * socket, or {@code null} if it is closed or not bound yet.
+ * @see #getLocalAddress()
+ * @see #getLocalPort()
+ * @see #bind(SocketAddress)
+ * @since 1.4
+ */
+
+ public SocketAddress getLocalSocketAddress() {
+ if (isClosed())
+ return null;
+ if (!isBound())
+ return null;
+ return new InetSocketAddress(getLocalAddress(), getLocalPort());
+ }
+
+ /**
+ * Sends a datagram packet from this socket. The
+ * {@code DatagramPacket} includes information indicating the
+ * data to be sent, its length, the IP address of the remote host,
+ * and the port number on the remote host.
+ *
+ * <p>If there is a security manager, and the socket is not currently
+ * connected to a remote address, this method first performs some
+ * security checks. First, if {@code p.getAddress().isMulticastAddress()}
+ * is true, this method calls the
+ * security manager's {@code checkMulticast} method
+ * with {@code p.getAddress()} as its argument.
+ * If the evaluation of that expression is false,
+ * this method instead calls the security manager's
+ * {@code checkConnect} method with arguments
+ * {@code p.getAddress().getHostAddress()} and
+ * {@code p.getPort()}. Each call to a security manager method
+ * could result in a SecurityException if the operation is not allowed.
+ *
+ * @param p the {@code DatagramPacket} to be sent.
+ *
+ * @exception IOException if an I/O error occurs.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkMulticast} or {@code checkConnect}
+ * method doesn't allow the send.
+ * @exception PortUnreachableException may be thrown if the socket is connected
+ * to a currently unreachable destination. Note, there is no
+ * guarantee that the exception will be thrown.
+ * @exception java.nio.channels.IllegalBlockingModeException
+ * if this socket has an associated channel,
+ * and the channel is in non-blocking mode.
+ * @exception IllegalArgumentException if the socket is connected,
+ * and connected address and packet address differ.
+ *
+ * @see java.net.DatagramPacket
+ * @see SecurityManager#checkMulticast(InetAddress)
+ * @see SecurityManager#checkConnect
+ * @revised 1.4
+ * @spec JSR-51
+ */
+ public void send(DatagramPacket p) throws IOException {
+ InetAddress packetAddress = null;
+ synchronized (p) {
+ // BEGIN Android-changed
+ if (pendingConnectException != null) {
+ throw new SocketException("Pending connect failure", pendingConnectException);
+ }
+ // END Android-changed
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ checkAddress (p.getAddress(), "send");
+ if (connectState == ST_NOT_CONNECTED) {
+ // check the address is ok wiht the security manager on every send.
+ SecurityManager security = System.getSecurityManager();
+
+ // The reason you want to synchronize on datagram packet
+ // is because you don't want an applet to change the address
+ // while you are trying to send the packet for example
+ // after the security check but before the send.
+ if (security != null) {
+ if (p.getAddress().isMulticastAddress()) {
+ security.checkMulticast(p.getAddress());
+ } else {
+ security.checkConnect(p.getAddress().getHostAddress(),
+ p.getPort());
+ }
+ }
+ } else {
+ // we're connected
+ packetAddress = p.getAddress();
+ if (packetAddress == null) {
+ p.setAddress(connectedAddress);
+ p.setPort(connectedPort);
+ } else if ((!packetAddress.equals(connectedAddress)) ||
+ p.getPort() != connectedPort) {
+ throw new IllegalArgumentException("connected address " +
+ "and packet address" +
+ " differ");
+ }
+ }
+ // Check whether the socket is bound
+ if (!isBound())
+ bind(new InetSocketAddress(0));
+ // call the method to send
+ getImpl().send(p);
+ }
+ }
+
+ /**
+ * Receives a datagram packet from this socket. When this method
+ * returns, the {@code DatagramPacket}'s buffer is filled with
+ * the data received. The datagram packet also contains the sender's
+ * IP address, and the port number on the sender's machine.
+ * <p>
+ * This method blocks until a datagram is received. The
+ * {@code length} field of the datagram packet object contains
+ * the length of the received message. If the message is longer than
+ * the packet's length, the message is truncated.
+ * <p>
+ * If there is a security manager, a packet cannot be received if the
+ * security manager's {@code checkAccept} method
+ * does not allow it.
+ *
+ * @param p the {@code DatagramPacket} into which to place
+ * the incoming data.
+ * @exception IOException if an I/O error occurs.
+ * @exception SocketTimeoutException if setSoTimeout was previously called
+ * and the timeout has expired.
+ * @exception PortUnreachableException may be thrown if the socket is connected
+ * to a currently unreachable destination. Note, there is no guarantee that the
+ * exception will be thrown.
+ * @exception java.nio.channels.IllegalBlockingModeException
+ * if this socket has an associated channel,
+ * and the channel is in non-blocking mode.
+ * @see java.net.DatagramPacket
+ * @see java.net.DatagramSocket
+ * @revised 1.4
+ * @spec JSR-51
+ */
+ public synchronized void receive(DatagramPacket p) throws IOException {
+ synchronized (p) {
+ if (!isBound())
+ bind(new InetSocketAddress(0));
+
+ // BEGIN Android-changed
+ if (pendingConnectException != null) {
+ throw new SocketException("Pending connect failure", pendingConnectException);
+ }
+ // END Android-changed
+
+ if (connectState == ST_NOT_CONNECTED) {
+ // check the address is ok with the security manager before every recv.
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ while(true) {
+ String peekAd = null;
+ int peekPort = 0;
+ // peek at the packet to see who it is from.
+ if (!oldImpl) {
+ // We can use the new peekData() API
+ DatagramPacket peekPacket = new DatagramPacket(new byte[1], 1);
+ peekPort = getImpl().peekData(peekPacket);
+ peekAd = peekPacket.getAddress().getHostAddress();
+ } else {
+ InetAddress adr = new InetAddress();
+ peekPort = getImpl().peek(adr);
+ peekAd = adr.getHostAddress();
+ }
+ try {
+ security.checkAccept(peekAd, peekPort);
+ // security check succeeded - so now break
+ // and recv the packet.
+ break;
+ } catch (SecurityException se) {
+ // Throw away the offending packet by consuming
+ // it in a tmp buffer.
+ DatagramPacket tmp = new DatagramPacket(new byte[1], 1);
+ getImpl().receive(tmp);
+
+ // silently discard the offending packet
+ // and continue: unknown/malicious
+ // entities on nets should not make
+ // runtime throw security exception and
+ // disrupt the applet by sending random
+ // datagram packets.
+ continue;
+ }
+ } // end of while
+ }
+ }
+ DatagramPacket tmp = null;
+ if ((connectState == ST_CONNECTED_NO_IMPL) || explicitFilter) {
+ // We have to do the filtering the old fashioned way since
+ // the native impl doesn't support connect or the connect
+ // via the impl failed, or .. "explicitFilter" may be set when
+ // a socket is connected via the impl, for a period of time
+ // when packets from other sources might be queued on socket.
+ boolean stop = false;
+ while (!stop) {
+ InetAddress peekAddress = null;
+ int peekPort = -1;
+ // peek at the packet to see who it is from.
+ if (!oldImpl) {
+ // We can use the new peekData() API
+ DatagramPacket peekPacket = new DatagramPacket(new byte[1], 1);
+ peekPort = getImpl().peekData(peekPacket);
+ peekAddress = peekPacket.getAddress();
+ } else {
+ // this api only works for IPv4
+ peekAddress = new InetAddress();
+ peekPort = getImpl().peek(peekAddress);
+ }
+ if ((!connectedAddress.equals(peekAddress)) ||
+ (connectedPort != peekPort)) {
+ // throw the packet away and silently continue
+ tmp = new DatagramPacket(
+ new byte[1024], 1024);
+ getImpl().receive(tmp);
+ if (explicitFilter) {
+ if (checkFiltering(tmp)) {
+ stop = true;
+ }
+ }
+ } else {
+ stop = true;
+ }
+ }
+ }
+ // If the security check succeeds, or the datagram is
+ // connected then receive the packet
+ getImpl().receive(p);
+ if (explicitFilter && tmp == null) {
+ // packet was not filtered, account for it here
+ checkFiltering(p);
+ }
+ }
+ }
+
+ private boolean checkFiltering(DatagramPacket p) throws SocketException {
+ bytesLeftToFilter -= p.getLength();
+ if (bytesLeftToFilter <= 0 || getImpl().dataAvailable() <= 0) {
+ explicitFilter = false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Gets the local address to which the socket is bound.
+ *
+ * <p>If there is a security manager, its
+ * {@code checkConnect} method is first called
+ * with the host address and {@code -1}
+ * as its arguments to see if the operation is allowed.
+ *
+ * @see SecurityManager#checkConnect
+ * @return the local address to which the socket is bound,
+ * {@code null} if the socket is closed, or
+ * an {@code InetAddress} representing
+ * {@link InetAddress#isAnyLocalAddress wildcard}
+ * address if either the socket is not bound, or
+ * the security manager {@code checkConnect}
+ * method does not allow the operation
+ * @since 1.1
+ */
+ public InetAddress getLocalAddress() {
+ if (isClosed())
+ return null;
+ InetAddress in = null;
+ try {
+ in = (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR);
+ if (in.isAnyLocalAddress()) {
+ in = InetAddress.anyLocalAddress();
+ }
+ SecurityManager s = System.getSecurityManager();
+ if (s != null) {
+ s.checkConnect(in.getHostAddress(), -1);
+ }
+ } catch (Exception e) {
+ in = InetAddress.anyLocalAddress(); // "0.0.0.0"
+ }
+ return in;
+ }
+
+ /**
+ * Returns the port number on the local host to which this socket
+ * is bound.
+ *
+ * @return the port number on the local host to which this socket is bound,
+ {@code -1} if the socket is closed, or
+ {@code 0} if it is not bound yet.
+ */
+ public int getLocalPort() {
+ if (isClosed())
+ return -1;
+ try {
+ return getImpl().getLocalPort();
+ } catch (Exception e) {
+ return 0;
+ }
+ }
+
+ /** Enable/disable SO_TIMEOUT with the specified timeout, in
+ * milliseconds. With this option set to a non-zero timeout,
+ * a call to receive() for this DatagramSocket
+ * will block for only this amount of time. If the timeout expires,
+ * a <B>java.net.SocketTimeoutException</B> is raised, though the
+ * DatagramSocket is still valid. The option <B>must</B> be enabled
+ * prior to entering the blocking operation to have effect. The
+ * timeout must be {@code > 0}.
+ * A timeout of zero is interpreted as an infinite timeout.
+ *
+ * @param timeout the specified timeout in milliseconds.
+ * @throws SocketException if there is an error in the underlying protocol, such as an UDP error.
+ * @since JDK1.1
+ * @see #getSoTimeout()
+ */
+ public synchronized void setSoTimeout(int timeout) throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ getImpl().setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout));
+ }
+
+ /**
+ * Retrieve setting for SO_TIMEOUT. 0 returns implies that the
+ * option is disabled (i.e., timeout of infinity).
+ *
+ * @return the setting for SO_TIMEOUT
+ * @throws SocketException if there is an error in the underlying protocol, such as an UDP error.
+ * @since JDK1.1
+ * @see #setSoTimeout(int)
+ */
+ public synchronized int getSoTimeout() throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ if (getImpl() == null)
+ return 0;
+ Object o = getImpl().getOption(SocketOptions.SO_TIMEOUT);
+ /* extra type safety */
+ if (o instanceof Integer) {
+ return ((Integer) o).intValue();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Sets the SO_SNDBUF option to the specified value for this
+ * {@code DatagramSocket}. The SO_SNDBUF option is used by the
+ * network implementation as a hint to size the underlying
+ * network I/O buffers. The SO_SNDBUF setting may also be used
+ * by the network implementation to determine the maximum size
+ * of the packet that can be sent on this socket.
+ * <p>
+ * As SO_SNDBUF is a hint, applications that want to verify
+ * what size the buffer is should call {@link #getSendBufferSize()}.
+ * <p>
+ * Increasing the buffer size may allow multiple outgoing packets
+ * to be queued by the network implementation when the send rate
+ * is high.
+ * <p>
+ * Note: If {@link #send(DatagramPacket)} is used to send a
+ * {@code DatagramPacket} that is larger than the setting
+ * of SO_SNDBUF then it is implementation specific if the
+ * packet is sent or discarded.
+ *
+ * @param size the size to which to set the send buffer
+ * size. This value must be greater than 0.
+ *
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as an UDP error.
+ * @exception IllegalArgumentException if the value is 0 or is
+ * negative.
+ * @see #getSendBufferSize()
+ */
+ public synchronized void setSendBufferSize(int size)
+ throws SocketException{
+ if (!(size > 0)) {
+ throw new IllegalArgumentException("negative send size");
+ }
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ getImpl().setOption(SocketOptions.SO_SNDBUF, new Integer(size));
+ }
+
+ /**
+ * Get value of the SO_SNDBUF option for this {@code DatagramSocket}, that is the
+ * buffer size used by the platform for output on this {@code DatagramSocket}.
+ *
+ * @return the value of the SO_SNDBUF option for this {@code DatagramSocket}
+ * @exception SocketException if there is an error in
+ * the underlying protocol, such as an UDP error.
+ * @see #setSendBufferSize
+ */
+ public synchronized int getSendBufferSize() throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ int result = 0;
+ Object o = getImpl().getOption(SocketOptions.SO_SNDBUF);
+ if (o instanceof Integer) {
+ result = ((Integer)o).intValue();
+ }
+ return result;
+ }
+
+ /**
+ * Sets the SO_RCVBUF option to the specified value for this
+ * {@code DatagramSocket}. The SO_RCVBUF option is used by the
+ * the network implementation as a hint to size the underlying
+ * network I/O buffers. The SO_RCVBUF setting may also be used
+ * by the network implementation to determine the maximum size
+ * of the packet that can be received on this socket.
+ * <p>
+ * Because SO_RCVBUF is a hint, applications that want to
+ * verify what size the buffers were set to should call
+ * {@link #getReceiveBufferSize()}.
+ * <p>
+ * Increasing SO_RCVBUF may allow the network implementation
+ * to buffer multiple packets when packets arrive faster than
+ * are being received using {@link #receive(DatagramPacket)}.
+ * <p>
+ * Note: It is implementation specific if a packet larger
+ * than SO_RCVBUF can be received.
+ *
+ * @param size the size to which to set the receive buffer
+ * size. This value must be greater than 0.
+ *
+ * @exception SocketException if there is an error in
+ * the underlying protocol, such as an UDP error.
+ * @exception IllegalArgumentException if the value is 0 or is
+ * negative.
+ * @see #getReceiveBufferSize()
+ */
+ public synchronized void setReceiveBufferSize(int size)
+ throws SocketException{
+ if (size <= 0) {
+ throw new IllegalArgumentException("invalid receive size");
+ }
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ getImpl().setOption(SocketOptions.SO_RCVBUF, new Integer(size));
+ }
+
+ /**
+ * Get value of the SO_RCVBUF option for this {@code DatagramSocket}, that is the
+ * buffer size used by the platform for input on this {@code DatagramSocket}.
+ *
+ * @return the value of the SO_RCVBUF option for this {@code DatagramSocket}
+ * @exception SocketException if there is an error in the underlying protocol, such as an UDP error.
+ * @see #setReceiveBufferSize(int)
+ */
+ public synchronized int getReceiveBufferSize()
+ throws SocketException{
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ int result = 0;
+ Object o = getImpl().getOption(SocketOptions.SO_RCVBUF);
+ if (o instanceof Integer) {
+ result = ((Integer)o).intValue();
+ }
+ return result;
+ }
+
+ /**
+ * Enable/disable the SO_REUSEADDR socket option.
+ * <p>
+ * For UDP sockets it may be necessary to bind more than one
+ * socket to the same socket address. This is typically for the
+ * purpose of receiving multicast packets
+ * (See {@link java.net.MulticastSocket}). The
+ * {@code SO_REUSEADDR} socket option allows multiple
+ * sockets to be bound to the same socket address if the
+ * {@code SO_REUSEADDR} socket option is enabled prior
+ * to binding the socket using {@link #bind(SocketAddress)}.
+ * <p>
+ * Note: This functionality is not supported by all existing platforms,
+ * so it is implementation specific whether this option will be ignored
+ * or not. However, if it is not supported then
+ * {@link #getReuseAddress()} will always return {@code false}.
+ * <p>
+ * When a {@code DatagramSocket} is created the initial setting
+ * of {@code SO_REUSEADDR} is disabled.
+ * <p>
+ * The behaviour when {@code SO_REUSEADDR} is enabled or
+ * disabled after a socket is bound (See {@link #isBound()})
+ * is not defined.
+ *
+ * @param on whether to enable or disable the
+ * @exception SocketException if an error occurs enabling or
+ * disabling the {@code SO_RESUEADDR} socket option,
+ * or the socket is closed.
+ * @since 1.4
+ * @see #getReuseAddress()
+ * @see #bind(SocketAddress)
+ * @see #isBound()
+ * @see #isClosed()
+ */
+ public synchronized void setReuseAddress(boolean on) throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ // Integer instead of Boolean for compatibility with older DatagramSocketImpl
+ if (oldImpl)
+ getImpl().setOption(SocketOptions.SO_REUSEADDR, new Integer(on?-1:0));
+ else
+ getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on));
+ }
+
+ /**
+ * Tests if SO_REUSEADDR is enabled.
+ *
+ * @return a {@code boolean} indicating whether or not SO_REUSEADDR is enabled.
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as an UDP error.
+ * @since 1.4
+ * @see #setReuseAddress(boolean)
+ */
+ public synchronized boolean getReuseAddress() throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ Object o = getImpl().getOption(SocketOptions.SO_REUSEADDR);
+ return ((Boolean)o).booleanValue();
+ }
+
+ /**
+ * Enable/disable SO_BROADCAST.
+ *
+ * <p> Some operating systems may require that the Java virtual machine be
+ * started with implementation specific privileges to enable this option or
+ * send broadcast datagrams.
+ *
+ * @param on
+ * whether or not to have broadcast turned on.
+ *
+ * @throws SocketException
+ * if there is an error in the underlying protocol, such as an UDP
+ * error.
+ *
+ * @since 1.4
+ * @see #getBroadcast()
+ */
+ public synchronized void setBroadcast(boolean on) throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ getImpl().setOption(SocketOptions.SO_BROADCAST, Boolean.valueOf(on));
+ }
+
+ /**
+ * Tests if SO_BROADCAST is enabled.
+ * @return a {@code boolean} indicating whether or not SO_BROADCAST is enabled.
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as an UDP error.
+ * @since 1.4
+ * @see #setBroadcast(boolean)
+ */
+ public synchronized boolean getBroadcast() throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ return ((Boolean)(getImpl().getOption(SocketOptions.SO_BROADCAST))).booleanValue();
+ }
+
+ /**
+ * Sets traffic class or type-of-service octet in the IP
+ * datagram header for datagrams sent from this DatagramSocket.
+ * As the underlying network implementation may ignore this
+ * value applications should consider it a hint.
+ *
+ * <P> The tc <B>must</B> be in the range {@code 0 <= tc <=
+ * 255} or an IllegalArgumentException will be thrown.
+ * <p>Notes:
+ * <p>For Internet Protocol v4 the value consists of an
+ * {@code integer}, the least significant 8 bits of which
+ * represent the value of the TOS octet in IP packets sent by
+ * the socket.
+ * RFC 1349 defines the TOS values as follows:
+ *
+ * <UL>
+ * <LI><CODE>IPTOS_LOWCOST (0x02)</CODE></LI>
+ * <LI><CODE>IPTOS_RELIABILITY (0x04)</CODE></LI>
+ * <LI><CODE>IPTOS_THROUGHPUT (0x08)</CODE></LI>
+ * <LI><CODE>IPTOS_LOWDELAY (0x10)</CODE></LI>
+ * </UL>
+ * The last low order bit is always ignored as this
+ * corresponds to the MBZ (must be zero) bit.
+ * <p>
+ * Setting bits in the precedence field may result in a
+ * SocketException indicating that the operation is not
+ * permitted.
+ * <p>
+ * for Internet Protocol v6 {@code tc} is the value that
+ * would be placed into the sin6_flowinfo field of the IP header.
+ *
+ * @param tc an {@code int} value for the bitset.
+ * @throws SocketException if there is an error setting the
+ * traffic class or type-of-service
+ * @since 1.4
+ * @see #getTrafficClass
+ */
+ public synchronized void setTrafficClass(int tc) throws SocketException {
+ if (tc < 0 || tc > 255)
+ throw new IllegalArgumentException("tc is not in range 0 -- 255");
+
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ try {
+ getImpl().setOption(SocketOptions.IP_TOS, tc);
+ } catch (SocketException se) {
+ // not supported if socket already connected
+ // Solaris returns error in such cases
+ if(!isConnected())
+ throw se;
+ }
+ }
+
+ /**
+ * Gets traffic class or type-of-service in the IP datagram
+ * header for packets sent from this DatagramSocket.
+ * <p>
+ * As the underlying network implementation may ignore the
+ * traffic class or type-of-service set using {@link #setTrafficClass(int)}
+ * this method may return a different value than was previously
+ * set using the {@link #setTrafficClass(int)} method on this
+ * DatagramSocket.
+ *
+ * @return the traffic class or type-of-service already set
+ * @throws SocketException if there is an error obtaining the
+ * traffic class or type-of-service value.
+ * @since 1.4
+ * @see #setTrafficClass(int)
+ */
+ public synchronized int getTrafficClass() throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ return ((Integer)(getImpl().getOption(SocketOptions.IP_TOS))).intValue();
+ }
+
+ /**
+ * Closes this datagram socket.
+ * <p>
+ * Any thread currently blocked in {@link #receive} upon this socket
+ * will throw a {@link SocketException}.
+ *
+ * <p> If this socket has an associated channel then the channel is closed
+ * as well.
+ *
+ * @revised 1.4
+ * @spec JSR-51
+ */
+ public void close() {
+ synchronized(closeLock) {
+ if (isClosed())
+ return;
+ impl.close();
+ closed = true;
+ }
+ }
+
+ /**
+ * Returns whether the socket is closed or not.
+ *
+ * @return true if the socket has been closed
+ * @since 1.4
+ */
+ public boolean isClosed() {
+ synchronized(closeLock) {
+ return closed;
+ }
+ }
+
+ /**
+ * Returns the unique {@link java.nio.channels.DatagramChannel} object
+ * associated with this datagram socket, if any.
+ *
+ * <p> A datagram socket will have a channel if, and only if, the channel
+ * itself was created via the {@link java.nio.channels.DatagramChannel#open
+ * DatagramChannel.open} method.
+ *
+ * @return the datagram channel associated with this datagram socket,
+ * or {@code null} if this socket was not created for a channel
+ *
+ * @since 1.4
+ * @spec JSR-51
+ */
+ public DatagramChannel getChannel() {
+ return null;
+ }
+
+ /**
+ * User defined factory for all datagram sockets.
+ */
+ static DatagramSocketImplFactory factory;
+
+ /**
+ * Sets the datagram socket implementation factory for the
+ * application. The factory can be specified only once.
+ * <p>
+ * When an application creates a new datagram socket, the socket
+ * implementation factory's {@code createDatagramSocketImpl} method is
+ * called to create the actual datagram socket implementation.
+ * <p>
+ * Passing {@code null} to the method is a no-op unless the factory
+ * was already set.
+ *
+ * <p>If there is a security manager, this method first calls
+ * the security manager's {@code checkSetFactory} method
+ * to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ *
+ * @param fac the desired factory.
+ * @exception IOException if an I/O error occurs when setting the
+ * datagram socket factory.
+ * @exception SocketException if the factory is already defined.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkSetFactory} method doesn't allow the
+ operation.
+ * @see
+ java.net.DatagramSocketImplFactory#createDatagramSocketImpl()
+ * @see SecurityManager#checkSetFactory
+ * @since 1.3
+ */
+ public static synchronized void
+ setDatagramSocketImplFactory(DatagramSocketImplFactory fac)
+ throws IOException
+ {
+ if (factory != null) {
+ throw new SocketException("factory already defined");
+ }
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkSetFactory();
+ }
+ factory = fac;
+ }
+
+ // Android-added: for testing and internal use.
+ /**
+ * @hide internal use only
+ */
+ public FileDescriptor getFileDescriptor$() {
+ return impl.fd;
+ }
+
+}
diff --git a/java/net/DatagramSocketImpl.java b/java/net/DatagramSocketImpl.java
new file mode 100644
index 0000000..537edfe
--- /dev/null
+++ b/java/net/DatagramSocketImpl.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+
+/**
+ * Abstract datagram and multicast socket implementation base class.
+ * @author Pavani Diwanji
+ * @since JDK1.1
+ */
+
+public abstract class DatagramSocketImpl implements SocketOptions {
+
+ /**
+ * The local port number.
+ */
+ protected int localPort;
+
+ /**
+ * The file descriptor object.
+ */
+ protected FileDescriptor fd;
+
+ int dataAvailable() {
+ // default impl returns zero, which disables the calling
+ // functionality
+ return 0;
+ }
+
+ /**
+ * The DatagramSocket or MulticastSocket
+ * that owns this impl
+ */
+ DatagramSocket socket;
+
+ void setDatagramSocket(DatagramSocket socket) {
+ this.socket = socket;
+ }
+
+ DatagramSocket getDatagramSocket() {
+ return socket;
+ }
+
+ /**
+ * Creates a datagram socket.
+ * @exception SocketException if there is an error in the
+ * underlying protocol, such as a TCP error.
+ */
+ protected abstract void create() throws SocketException;
+
+ /**
+ * Binds a datagram socket to a local port and address.
+ * @param lport the local port
+ * @param laddr the local address
+ * @exception SocketException if there is an error in the
+ * underlying protocol, such as a TCP error.
+ */
+ protected abstract void bind(int lport, InetAddress laddr) throws SocketException;
+
+ /**
+ * Sends a datagram packet. The packet contains the data and the
+ * destination address to send the packet to.
+ * @param p the packet to be sent.
+ * @exception IOException if an I/O exception occurs while sending the
+ * datagram packet.
+ * @exception PortUnreachableException may be thrown if the socket is connected
+ * to a currently unreachable destination. Note, there is no guarantee that
+ * the exception will be thrown.
+ */
+ protected abstract void send(DatagramPacket p) throws IOException;
+
+ /**
+ * Connects a datagram socket to a remote destination. This associates the remote
+ * address with the local socket so that datagrams may only be sent to this destination
+ * and received from this destination. This may be overridden to call a native
+ * system connect.
+ *
+ * <p>If the remote destination to which the socket is connected does not
+ * exist, or is otherwise unreachable, and if an ICMP destination unreachable
+ * packet has been received for that address, then a subsequent call to
+ * send or receive may throw a PortUnreachableException.
+ * Note, there is no guarantee that the exception will be thrown.
+ * @param address the remote InetAddress to connect to
+ * @param port the remote port number
+ * @exception SocketException may be thrown if the socket cannot be
+ * connected to the remote destination
+ * @since 1.4
+ */
+ protected void connect(InetAddress address, int port) throws SocketException {}
+
+ /**
+ * Disconnects a datagram socket from its remote destination.
+ * @since 1.4
+ */
+ protected void disconnect() {}
+
+ /**
+ * Peek at the packet to see who it is from. Updates the specified {@code InetAddress}
+ * to the address which the packet came from.
+ * @param i an InetAddress object
+ * @return the port number which the packet came from.
+ * @exception IOException if an I/O exception occurs
+ * @exception PortUnreachableException may be thrown if the socket is connected
+ * to a currently unreachable destination. Note, there is no guarantee that the
+ * exception will be thrown.
+ */
+ protected abstract int peek(InetAddress i) throws IOException;
+
+ /**
+ * Peek at the packet to see who it is from. The data is copied into the specified
+ * {@code DatagramPacket}. The data is returned,
+ * but not consumed, so that a subsequent peekData/receive operation
+ * will see the same data.
+ * @param p the Packet Received.
+ * @return the port number which the packet came from.
+ * @exception IOException if an I/O exception occurs
+ * @exception PortUnreachableException may be thrown if the socket is connected
+ * to a currently unreachable destination. Note, there is no guarantee that the
+ * exception will be thrown.
+ * @since 1.4
+ */
+ protected abstract int peekData(DatagramPacket p) throws IOException;
+ /**
+ * Receive the datagram packet.
+ * @param p the Packet Received.
+ * @exception IOException if an I/O exception occurs
+ * while receiving the datagram packet.
+ * @exception PortUnreachableException may be thrown if the socket is connected
+ * to a currently unreachable destination. Note, there is no guarantee that the
+ * exception will be thrown.
+ */
+ protected abstract void receive(DatagramPacket p) throws IOException;
+
+ /**
+ * Set the TTL (time-to-live) option.
+ * @param ttl a byte specifying the TTL value
+ *
+ * @deprecated use setTimeToLive instead.
+ * @exception IOException if an I/O exception occurs while setting
+ * the time-to-live option.
+ * @see #getTTL()
+ */
+ @Deprecated
+ protected abstract void setTTL(byte ttl) throws IOException;
+
+ /**
+ * Retrieve the TTL (time-to-live) option.
+ *
+ * @exception IOException if an I/O exception occurs
+ * while retrieving the time-to-live option
+ * @deprecated use getTimeToLive instead.
+ * @return a byte representing the TTL value
+ * @see #setTTL(byte)
+ */
+ @Deprecated
+ protected abstract byte getTTL() throws IOException;
+
+ /**
+ * Set the TTL (time-to-live) option.
+ * @param ttl an {@code int} specifying the time-to-live value
+ * @exception IOException if an I/O exception occurs
+ * while setting the time-to-live option.
+ * @see #getTimeToLive()
+ */
+ protected abstract void setTimeToLive(int ttl) throws IOException;
+
+ /**
+ * Retrieve the TTL (time-to-live) option.
+ * @exception IOException if an I/O exception occurs
+ * while retrieving the time-to-live option
+ * @return an {@code int} representing the time-to-live value
+ * @see #setTimeToLive(int)
+ */
+ protected abstract int getTimeToLive() throws IOException;
+
+ /**
+ * Join the multicast group.
+ * @param inetaddr multicast address to join.
+ * @exception IOException if an I/O exception occurs
+ * while joining the multicast group.
+ */
+ protected abstract void join(InetAddress inetaddr) throws IOException;
+
+ /**
+ * Leave the multicast group.
+ * @param inetaddr multicast address to leave.
+ * @exception IOException if an I/O exception occurs
+ * while leaving the multicast group.
+ */
+ protected abstract void leave(InetAddress inetaddr) throws IOException;
+
+ /**
+ * Join the multicast group.
+ * @param mcastaddr address to join.
+ * @param netIf specifies the local interface to receive multicast
+ * datagram packets
+ * @throws IOException if an I/O exception occurs while joining
+ * the multicast group
+ * @since 1.4
+ */
+ protected abstract void joinGroup(SocketAddress mcastaddr,
+ NetworkInterface netIf)
+ throws IOException;
+
+ /**
+ * Leave the multicast group.
+ * @param mcastaddr address to leave.
+ * @param netIf specified the local interface to leave the group at
+ * @throws IOException if an I/O exception occurs while leaving
+ * the multicast group
+ * @since 1.4
+ */
+ protected abstract void leaveGroup(SocketAddress mcastaddr,
+ NetworkInterface netIf)
+ throws IOException;
+
+ /**
+ * Close the socket.
+ */
+ protected abstract void close();
+
+ /**
+ * Gets the local port.
+ * @return an {@code int} representing the local port value
+ */
+ protected int getLocalPort() {
+ return localPort;
+ }
+
+ <T> void setOption(SocketOption<T> name, T value) throws IOException {
+ if (name == StandardSocketOptions.SO_SNDBUF) {
+ setOption(SocketOptions.SO_SNDBUF, value);
+ } else if (name == StandardSocketOptions.SO_RCVBUF) {
+ setOption(SocketOptions.SO_RCVBUF, value);
+ } else if (name == StandardSocketOptions.SO_REUSEADDR) {
+ setOption(SocketOptions.SO_REUSEADDR, value);
+ } else if (name == StandardSocketOptions.IP_TOS) {
+ setOption(SocketOptions.IP_TOS, value);
+ } else if (name == StandardSocketOptions.IP_MULTICAST_IF &&
+ (getDatagramSocket() instanceof MulticastSocket)) {
+ setOption(SocketOptions.IP_MULTICAST_IF2, value);
+ } else if (name == StandardSocketOptions.IP_MULTICAST_TTL &&
+ (getDatagramSocket() instanceof MulticastSocket)) {
+ if (! (value instanceof Integer)) {
+ throw new IllegalArgumentException("not an integer");
+ }
+ setTimeToLive((Integer)value);
+ } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP &&
+ (getDatagramSocket() instanceof MulticastSocket)) {
+ setOption(SocketOptions.IP_MULTICAST_LOOP, value);
+ } else {
+ throw new UnsupportedOperationException("unsupported option");
+ }
+ }
+
+ <T> T getOption(SocketOption<T> name) throws IOException {
+ if (name == StandardSocketOptions.SO_SNDBUF) {
+ return (T) getOption(SocketOptions.SO_SNDBUF);
+ } else if (name == StandardSocketOptions.SO_RCVBUF) {
+ return (T) getOption(SocketOptions.SO_RCVBUF);
+ } else if (name == StandardSocketOptions.SO_REUSEADDR) {
+ return (T) getOption(SocketOptions.SO_REUSEADDR);
+ } else if (name == StandardSocketOptions.IP_TOS) {
+ return (T) getOption(SocketOptions.IP_TOS);
+ } else if (name == StandardSocketOptions.IP_MULTICAST_IF &&
+ (getDatagramSocket() instanceof MulticastSocket)) {
+ return (T) getOption(SocketOptions.IP_MULTICAST_IF2);
+ } else if (name == StandardSocketOptions.IP_MULTICAST_TTL &&
+ (getDatagramSocket() instanceof MulticastSocket)) {
+ Integer ttl = getTimeToLive();
+ return (T)ttl;
+ } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP &&
+ (getDatagramSocket() instanceof MulticastSocket)) {
+ return (T) getOption(SocketOptions.IP_MULTICAST_LOOP);
+ } else {
+ throw new UnsupportedOperationException("unsupported option");
+ }
+ }
+
+ /**
+ * Gets the datagram socket file descriptor.
+ * @return a {@code FileDescriptor} object representing the datagram socket
+ * file descriptor
+ */
+ protected FileDescriptor getFileDescriptor() {
+ return fd;
+ }
+}
diff --git a/java/net/DatagramSocketImplFactory.java b/java/net/DatagramSocketImplFactory.java
new file mode 100644
index 0000000..4d89196
--- /dev/null
+++ b/java/net/DatagramSocketImplFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * This interface defines a factory for datagram socket implementations. It
+ * is used by the classes {@code DatagramSocket} to create actual socket
+ * implementations.
+ *
+ * @author Yingxian Wang
+ * @see java.net.DatagramSocket
+ * @since 1.3
+ */
+public
+interface DatagramSocketImplFactory {
+ /**
+ * Creates a new {@code DatagramSocketImpl} instance.
+ *
+ * @return a new instance of {@code DatagramSocketImpl}.
+ * @see java.net.DatagramSocketImpl
+ */
+ DatagramSocketImpl createDatagramSocketImpl();
+}
diff --git a/java/net/DefaultDatagramSocketImplFactory.java b/java/net/DefaultDatagramSocketImplFactory.java
new file mode 100644
index 0000000..ad62c58
--- /dev/null
+++ b/java/net/DefaultDatagramSocketImplFactory.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2007,2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.net;
+
+import java.security.AccessController;
+
+/**
+ * This class defines a factory for creating DatagramSocketImpls. It defaults
+ * to creating plain DatagramSocketImpls, but may create other DatagramSocketImpls
+ * by setting the impl.prefix system property.
+ *
+ * @author Chris Hegarty
+ */
+
+class DefaultDatagramSocketImplFactory {
+ static Class<?> prefixImplClass = null;
+
+ static {
+ String prefix = null;
+ try {
+ prefix = AccessController.doPrivileged(
+ new sun.security.action.GetPropertyAction("impl.prefix", null));
+ if (prefix != null)
+ prefixImplClass = Class.forName("java.net."+prefix+"DatagramSocketImpl");
+ } catch (Exception e) {
+ System.err.println("Can't find class: java.net." +
+ prefix +
+ "DatagramSocketImpl: check impl.prefix property");
+ //prefixImplClass = null;
+ }
+ }
+
+ /**
+ * Creates a new <code>DatagramSocketImpl</code> instance.
+ *
+ * @param isMulticast true if this impl if for a MutlicastSocket
+ * @return a new instance of a <code>DatagramSocketImpl</code>.
+ */
+ static DatagramSocketImpl createDatagramSocketImpl(boolean isMulticast /*unused on unix*/)
+ throws SocketException {
+ if (prefixImplClass != null) {
+ try {
+ return (DatagramSocketImpl)prefixImplClass.newInstance();
+ } catch (Exception e) {
+ throw new SocketException("can't instantiate DatagramSocketImpl");
+ }
+ } else {
+ return new java.net.PlainDatagramSocketImpl();
+ }
+ }
+}
diff --git a/java/net/DefaultFileNameMap.java b/java/net/DefaultFileNameMap.java
new file mode 100644
index 0000000..da67d14
--- /dev/null
+++ b/java/net/DefaultFileNameMap.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 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 libcore.content.type.MimeMap;
+
+/**
+ * Implements {@link FileNameMap} in terms of {@link MimeMap}.
+ */
+class DefaultFileNameMap implements FileNameMap {
+
+ public String getContentTypeFor(String filename) {
+ int fragmentIndex = filename.indexOf('#');
+ if (fragmentIndex >= 0) {
+ filename = filename.substring(0, fragmentIndex);
+ }
+ if (filename.endsWith("/")) { // a directory
+ return "text/html";
+ }
+
+ int slashIndex = filename.lastIndexOf('/');
+ if (slashIndex >= 0) {
+ filename = filename.substring(slashIndex);
+ }
+
+ MimeMap mimeMap = MimeMap.getDefault();
+ int dotIndex = -1;
+ do {
+ String ext = filename.substring(dotIndex + 1);
+ String result = mimeMap.guessMimeTypeFromExtension(ext);
+ if ((result != null) &&
+ // Compat behavior: If there's a '/', then extension must not be the
+ // whole string. http://b/144977800
+ (slashIndex < 0 || dotIndex >= 0)) {
+ return result;
+ }
+ dotIndex = filename.indexOf('.', dotIndex + 1);
+ } while (dotIndex >= 0);
+ return null;
+ }
+}
diff --git a/java/net/DefaultInterface.java b/java/net/DefaultInterface.java
new file mode 100644
index 0000000..0c31cb6
--- /dev/null
+++ b/java/net/DefaultInterface.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * Choose a network interface to be the default for
+ * outgoing IPv6 traffic that does not specify a scope_id (and which needs one).
+ *
+ * Platforms that do not require a default interface may return null
+ * which is what this implementation does.
+ */
+
+class DefaultInterface {
+
+ static NetworkInterface getDefault() {
+ return null;
+ }
+}
diff --git a/java/net/FileNameMap.java b/java/net/FileNameMap.java
new file mode 100644
index 0000000..393b5aa
--- /dev/null
+++ b/java/net/FileNameMap.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * A simple interface which provides a mechanism to map
+ * between a file name and a MIME type string.
+ *
+ * @author Steven B. Byrne
+ * @since JDK1.1
+ */
+public interface FileNameMap {
+
+ /**
+ * Gets the MIME type for the specified file name.
+ * @param fileName the specified file name
+ * @return a {@code String} indicating the MIME
+ * type for the specified file name.
+ */
+ public String getContentTypeFor(String fileName);
+}
diff --git a/java/net/HttpCookie.java b/java/net/HttpCookie.java
new file mode 100644
index 0000000..3ff33c7
--- /dev/null
+++ b/java/net/HttpCookie.java
@@ -0,0 +1,1213 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+import libcore.net.http.HttpDate;
+
+/**
+ * An HttpCookie object represents an HTTP cookie, which carries state
+ * information between server and user agent. Cookie is widely adopted
+ * to create stateful sessions.
+ *
+ * <p> There are 3 HTTP cookie specifications:
+ * <blockquote>
+ * Netscape draft<br>
+ * RFC 2109 - <a href="http://www.ietf.org/rfc/rfc2109.txt">
+ * <i>http://www.ietf.org/rfc/rfc2109.txt</i></a><br>
+ * RFC 2965 - <a href="http://www.ietf.org/rfc/rfc2965.txt">
+ * <i>http://www.ietf.org/rfc/rfc2965.txt</i></a>
+ * </blockquote>
+ *
+ * <p> HttpCookie class can accept all these 3 forms of syntax.
+ *
+ * @author Edward Wang
+ * @since 1.6
+ */
+public final class HttpCookie implements Cloneable {
+ // BEGIN Android-added: Reserved name can't be HttpCookie name
+ private static final Set<String> RESERVED_NAMES = new HashSet<String>();
+
+ static {
+ RESERVED_NAMES.add("comment"); // RFC 2109 RFC 2965 RFC 6265
+ RESERVED_NAMES.add("commenturl"); // RFC 2965 RFC 6265
+ RESERVED_NAMES.add("discard"); // RFC 2965 RFC 6265
+ RESERVED_NAMES.add("domain"); // Netscape RFC 2109 RFC 2965 RFC 6265
+ RESERVED_NAMES.add("expires"); // Netscape
+ RESERVED_NAMES.add("httponly"); // RFC 6265
+ RESERVED_NAMES.add("max-age"); // RFC 2109 RFC 2965 RFC 6265
+ RESERVED_NAMES.add("path"); // Netscape RFC 2109 RFC 2965 RFC 6265
+ RESERVED_NAMES.add("port"); // RFC 2965 RFC 6265
+ RESERVED_NAMES.add("secure"); // Netscape RFC 2109 RFC 2965 RFC 6265
+ RESERVED_NAMES.add("version"); // RFC 2109 RFC 2965 RFC 6265
+ }
+ // END Android-added: Reserved name can't be HttpCookie name
+
+ // ---------------- Fields --------------
+
+ // The value of the cookie itself.
+ private final String name; // NAME= ... "$Name" style is reserved
+ private String value; // value of NAME
+
+ // Attributes encoded in the header's cookie fields.
+ private String comment; // Comment=VALUE ... describes cookie's use
+ private String commentURL; // CommentURL="http URL" ... describes cookie's use
+ private boolean toDiscard; // Discard ... discard cookie unconditionally
+ private String domain; // Domain=VALUE ... domain that sees cookie
+ private long maxAge = MAX_AGE_UNSPECIFIED; // Max-Age=VALUE ... cookies auto-expire
+ private String path; // Path=VALUE ... URLs that see the cookie
+ private String portlist; // Port[="portlist"] ... the port cookie may be returned to
+ private boolean secure; // Secure ... e.g. use SSL
+ private boolean httpOnly; // HttpOnly ... i.e. not accessible to scripts
+ private int version = 1; // Version=1 ... RFC 2965 style
+
+ // The original header this cookie was consructed from, if it was
+ // constructed by parsing a header, otherwise null.
+ private final String header;
+
+ //
+ // Android-changed: Fixed units, s/seconds/milliseconds/, in comment below.
+ // Hold the creation time (in milliseconds) of the http cookie for later
+ // expiration calculation
+ private final long whenCreated;
+
+ // Since the positive and zero max-age have their meanings,
+ // this value serves as a hint as 'not specify max-age'
+ private final static long MAX_AGE_UNSPECIFIED = -1;
+
+ // BEGIN Android-removed: Use libcore.net.http.HttpDate for parsing.
+ // date formats used by Netscape's cookie draft
+ // as well as formats seen on various sites
+ /*
+ private final static String[] COOKIE_DATE_FORMATS = {
+ "EEE',' dd-MMM-yyyy HH:mm:ss 'GMT'",
+ "EEE',' dd MMM yyyy HH:mm:ss 'GMT'",
+ "EEE MMM dd yyyy HH:mm:ss 'GMT'Z",
+ "EEE',' dd-MMM-yy HH:mm:ss 'GMT'",
+ "EEE',' dd MMM yy HH:mm:ss 'GMT'",
+ "EEE MMM dd yy HH:mm:ss 'GMT'Z"
+ };
+ */
+ // END Android-removed: Use libcore.net.http.HttpDate for parsing.
+
+ // constant strings represent set-cookie header token
+ private final static String SET_COOKIE = "set-cookie:";
+ private final static String SET_COOKIE2 = "set-cookie2:";
+
+ // ---------------- Ctors --------------
+
+ /**
+ * Constructs a cookie with a specified name and value.
+ *
+ * <p> The name must conform to RFC 2965. That means it can contain
+ * only ASCII alphanumeric characters and cannot contain commas,
+ * semicolons, or white space or begin with a $ character. The cookie's
+ * name cannot be changed after creation.
+ *
+ * <p> The value can be anything the server chooses to send. Its
+ * value is probably of interest only to the server. The cookie's
+ * value can be changed after creation with the
+ * {@code setValue} method.
+ *
+ * <p> By default, cookies are created according to the RFC 2965
+ * cookie specification. The version can be changed with the
+ * {@code setVersion} method.
+ *
+ *
+ * @param name
+ * a {@code String} specifying the name of the cookie
+ *
+ * @param value
+ * a {@code String} specifying the value of the cookie
+ *
+ * @throws IllegalArgumentException
+ * if the cookie name contains illegal characters
+ * @throws NullPointerException
+ * if {@code name} is {@code null}
+ *
+ * @see #setValue
+ * @see #setVersion
+ */
+ public HttpCookie(String name, String value) {
+ this(name, value, null /*header*/);
+ }
+
+ private HttpCookie(String name, String value, String header) {
+ name = name.trim();
+ if (name.length() == 0 || !isToken(name) || name.charAt(0) == '$') {
+ throw new IllegalArgumentException("Illegal cookie name");
+ }
+
+ this.name = name;
+ this.value = value;
+ toDiscard = false;
+ secure = false;
+
+ whenCreated = System.currentTimeMillis();
+ portlist = null;
+ this.header = header;
+ }
+
+ /**
+ * Constructs cookies from set-cookie or set-cookie2 header string.
+ * RFC 2965 section 3.2.2 set-cookie2 syntax indicates that one header line
+ * may contain more than one cookie definitions, so this is a static
+ * utility method instead of another constructor.
+ *
+ * @param header
+ * a {@code String} specifying the set-cookie header. The header
+ * should start with "set-cookie", or "set-cookie2" token; or it
+ * should have no leading token at all.
+ *
+ * @return a List of cookie parsed from header line string
+ *
+ * @throws IllegalArgumentException
+ * if header string violates the cookie specification's syntax or
+ * the cookie name contains illegal characters.
+ * @throws NullPointerException
+ * if the header string is {@code null}
+ */
+ public static List<HttpCookie> parse(String header) {
+ return parse(header, false);
+ }
+
+ // Private version of parse() that will store the original header used to
+ // create the cookie, in the cookie itself. This can be useful for filtering
+ // Set-Cookie[2] headers, using the internal parsing logic defined in this
+ // class.
+ private static List<HttpCookie> parse(String header, boolean retainHeader) {
+
+ int version = guessCookieVersion(header);
+
+ // if header start with set-cookie or set-cookie2, strip it off
+ if (startsWithIgnoreCase(header, SET_COOKIE2)) {
+ header = header.substring(SET_COOKIE2.length());
+ } else if (startsWithIgnoreCase(header, SET_COOKIE)) {
+ header = header.substring(SET_COOKIE.length());
+ }
+
+ List<HttpCookie> cookies = new java.util.ArrayList<>();
+ // The Netscape cookie may have a comma in its expires attribute, while
+ // the comma is the delimiter in rfc 2965/2109 cookie header string.
+ // so the parse logic is slightly different
+ if (version == 0) {
+ // Netscape draft cookie
+ HttpCookie cookie = parseInternal(header, retainHeader);
+ cookie.setVersion(0);
+ cookies.add(cookie);
+ } else {
+ // rfc2965/2109 cookie
+ // if header string contains more than one cookie,
+ // it'll separate them with comma
+ List<String> cookieStrings = splitMultiCookies(header);
+ for (String cookieStr : cookieStrings) {
+ HttpCookie cookie = parseInternal(cookieStr, retainHeader);
+ cookie.setVersion(1);
+ cookies.add(cookie);
+ }
+ }
+
+ return cookies;
+ }
+
+ // ---------------- Public operations --------------
+
+ /**
+ * Reports whether this HTTP cookie has expired or not.
+ *
+ * @return {@code true} to indicate this HTTP cookie has expired;
+ * otherwise, {@code false}
+ */
+ public boolean hasExpired() {
+ if (maxAge == 0) return true;
+
+ // if not specify max-age, this cookie should be
+ // discarded when user agent is to be closed, but
+ // it is not expired.
+ if (maxAge == MAX_AGE_UNSPECIFIED) return false;
+
+ long deltaSecond = (System.currentTimeMillis() - whenCreated) / 1000;
+ if (deltaSecond > maxAge)
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * Specifies a comment that describes a cookie's purpose.
+ * The comment is useful if the browser presents the cookie
+ * to the user. Comments are not supported by Netscape Version 0 cookies.
+ *
+ * @param purpose
+ * a {@code String} specifying the comment to display to the user
+ *
+ * @see #getComment
+ */
+ public void setComment(String purpose) {
+ comment = purpose;
+ }
+
+ /**
+ * Returns the comment describing the purpose of this cookie, or
+ * {@code null} if the cookie has no comment.
+ *
+ * @return a {@code String} containing the comment, or {@code null} if none
+ *
+ * @see #setComment
+ */
+ public String getComment() {
+ return comment;
+ }
+
+ /**
+ * Specifies a comment URL that describes a cookie's purpose.
+ * The comment URL is useful if the browser presents the cookie
+ * to the user. Comment URL is RFC 2965 only.
+ *
+ * @param purpose
+ * a {@code String} specifying the comment URL to display to the user
+ *
+ * @see #getCommentURL
+ */
+ public void setCommentURL(String purpose) {
+ commentURL = purpose;
+ }
+
+ /**
+ * Returns the comment URL describing the purpose of this cookie, or
+ * {@code null} if the cookie has no comment URL.
+ *
+ * @return a {@code String} containing the comment URL, or {@code null}
+ * if none
+ *
+ * @see #setCommentURL
+ */
+ public String getCommentURL() {
+ return commentURL;
+ }
+
+ /**
+ * Specify whether user agent should discard the cookie unconditionally.
+ * This is RFC 2965 only attribute.
+ *
+ * @param discard
+ * {@code true} indicates to discard cookie unconditionally
+ *
+ * @see #getDiscard
+ */
+ public void setDiscard(boolean discard) {
+ toDiscard = discard;
+ }
+
+ /**
+ * Returns the discard attribute of the cookie
+ *
+ * @return a {@code boolean} to represent this cookie's discard attribute
+ *
+ * @see #setDiscard
+ */
+ public boolean getDiscard() {
+ return toDiscard;
+ }
+
+ /**
+ * Specify the portlist of the cookie, which restricts the port(s)
+ * to which a cookie may be sent back in a Cookie header.
+ *
+ * @param ports
+ * a {@code String} specify the port list, which is comma separated
+ * series of digits
+ *
+ * @see #getPortlist
+ */
+ public void setPortlist(String ports) {
+ portlist = ports;
+ }
+
+ /**
+ * Returns the port list attribute of the cookie
+ *
+ * @return a {@code String} contains the port list or {@code null} if none
+ *
+ * @see #setPortlist
+ */
+ public String getPortlist() {
+ return portlist;
+ }
+
+ /**
+ * Specifies the domain within which this cookie should be presented.
+ *
+ * <p> The form of the domain name is specified by RFC 2965. A domain
+ * name begins with a dot ({@code .foo.com}) and means that
+ * the cookie is visible to servers in a specified Domain Name System
+ * (DNS) zone (for example, {@code www.foo.com}, but not
+ * {@code a.b.foo.com}). By default, cookies are only returned
+ * to the server that sent them.
+ *
+ * @param pattern
+ * a {@code String} containing the domain name within which this
+ * cookie is visible; form is according to RFC 2965
+ *
+ * @see #getDomain
+ */
+ public void setDomain(String pattern) {
+ if (pattern != null)
+ domain = pattern.toLowerCase();
+ else
+ domain = pattern;
+ }
+
+ /**
+ * Returns the domain name set for this cookie. The form of the domain name
+ * is set by RFC 2965.
+ *
+ * @return a {@code String} containing the domain name
+ *
+ * @see #setDomain
+ */
+ public String getDomain() {
+ return domain;
+ }
+
+ /**
+ * Sets the maximum age of the cookie in seconds.
+ *
+ * <p> A positive value indicates that the cookie will expire
+ * after that many seconds have passed. Note that the value is
+ * the <i>maximum</i> age when the cookie will expire, not the cookie's
+ * current age.
+ *
+ * <p> A negative value means that the cookie is not stored persistently
+ * and will be deleted when the Web browser exits. A zero value causes the
+ * cookie to be deleted.
+ *
+ * @param expiry
+ * an integer specifying the maximum age of the cookie in seconds;
+ * if zero, the cookie should be discarded immediately; otherwise,
+ * the cookie's max age is unspecified.
+ *
+ * @see #getMaxAge
+ */
+ public void setMaxAge(long expiry) {
+ maxAge = expiry;
+ }
+
+ /**
+ * Returns the maximum age of the cookie, specified in seconds. By default,
+ * {@code -1} indicating the cookie will persist until browser shutdown.
+ *
+ * @return an integer specifying the maximum age of the cookie in seconds
+ *
+ * @see #setMaxAge
+ */
+ public long getMaxAge() {
+ return maxAge;
+ }
+
+ /**
+ * Specifies a path for the cookie to which the client should return
+ * the cookie.
+ *
+ * <p> The cookie is visible to all the pages in the directory
+ * you specify, and all the pages in that directory's subdirectories.
+ * A cookie's path must include the servlet that set the cookie,
+ * for example, <i>/catalog</i>, which makes the cookie
+ * visible to all directories on the server under <i>/catalog</i>.
+ *
+ * <p> Consult RFC 2965 (available on the Internet) for more
+ * information on setting path names for cookies.
+ *
+ * @param uri
+ * a {@code String} specifying a path
+ *
+ * @see #getPath
+ */
+ public void setPath(String uri) {
+ path = uri;
+ }
+
+ /**
+ * Returns the path on the server to which the browser returns this cookie.
+ * The cookie is visible to all subpaths on the server.
+ *
+ * @return a {@code String} specifying a path that contains a servlet name,
+ * for example, <i>/catalog</i>
+ *
+ * @see #setPath
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * Indicates whether the cookie should only be sent using a secure protocol,
+ * such as HTTPS or SSL.
+ *
+ * <p> The default value is {@code false}.
+ *
+ * @param flag
+ * If {@code true}, the cookie can only be sent over a secure
+ * protocol like HTTPS. If {@code false}, it can be sent over
+ * any protocol.
+ *
+ * @see #getSecure
+ */
+ public void setSecure(boolean flag) {
+ secure = flag;
+ }
+
+ /**
+ * Returns {@code true} if sending this cookie should be restricted to a
+ * secure protocol, or {@code false} if the it can be sent using any
+ * protocol.
+ *
+ * @return {@code false} if the cookie can be sent over any standard
+ * protocol; otherwise, {@code true}
+ *
+ * @see #setSecure
+ */
+ public boolean getSecure() {
+ return secure;
+ }
+
+ /**
+ * Returns the name of the cookie. The name cannot be changed after
+ * creation.
+ *
+ * @return a {@code String} specifying the cookie's name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Assigns a new value to a cookie after the cookie is created.
+ * If you use a binary value, you may want to use BASE64 encoding.
+ *
+ * <p> With Version 0 cookies, values should not contain white space,
+ * brackets, parentheses, equals signs, commas, double quotes, slashes,
+ * question marks, at signs, colons, and semicolons. Empty values may not
+ * behave the same way on all browsers.
+ *
+ * @param newValue
+ * a {@code String} specifying the new value
+ *
+ * @see #getValue
+ */
+ public void setValue(String newValue) {
+ value = newValue;
+ }
+
+ /**
+ * Returns the value of the cookie.
+ *
+ * @return a {@code String} containing the cookie's present value
+ *
+ * @see #setValue
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * Returns the version of the protocol this cookie complies with. Version 1
+ * complies with RFC 2965/2109, and version 0 complies with the original
+ * cookie specification drafted by Netscape. Cookies provided by a browser
+ * use and identify the browser's cookie version.
+ *
+ * @return 0 if the cookie complies with the original Netscape
+ * specification; 1 if the cookie complies with RFC 2965/2109
+ *
+ * @see #setVersion
+ */
+ public int getVersion() {
+ return version;
+ }
+
+ /**
+ * Sets the version of the cookie protocol this cookie complies
+ * with. Version 0 complies with the original Netscape cookie
+ * specification. Version 1 complies with RFC 2965/2109.
+ *
+ * @param v
+ * 0 if the cookie should comply with the original Netscape
+ * specification; 1 if the cookie should comply with RFC 2965/2109
+ *
+ * @throws IllegalArgumentException
+ * if {@code v} is neither 0 nor 1
+ *
+ * @see #getVersion
+ */
+ public void setVersion(int v) {
+ if (v != 0 && v != 1) {
+ throw new IllegalArgumentException("cookie version should be 0 or 1");
+ }
+
+ version = v;
+ }
+
+ /**
+ * Returns {@code true} if this cookie contains the <i>HttpOnly</i>
+ * attribute. This means that the cookie should not be accessible to
+ * scripting engines, like javascript.
+ *
+ * @return {@code true} if this cookie should be considered HTTPOnly
+ *
+ * @see #setHttpOnly(boolean)
+ */
+ public boolean isHttpOnly() {
+ return httpOnly;
+ }
+
+ /**
+ * Indicates whether the cookie should be considered HTTP Only. If set to
+ * {@code true} it means the cookie should not be accessible to scripting
+ * engines like javascript.
+ *
+ * @param httpOnly
+ * if {@code true} make the cookie HTTP only, i.e. only visible as
+ * part of an HTTP request.
+ *
+ * @see #isHttpOnly()
+ */
+ public void setHttpOnly(boolean httpOnly) {
+ this.httpOnly = httpOnly;
+ }
+
+ /**
+ * The utility method to check whether a host name is in a domain or not.
+ *
+ * <p> This concept is described in the cookie specification.
+ * To understand the concept, some terminologies need to be defined first:
+ * <blockquote>
+ * effective host name = hostname if host name contains dot<br>
+ *
+ * or = hostname.local if not
+ * </blockquote>
+ * <p>Host A's name domain-matches host B's if:
+ * <blockquote><ul>
+ * <li>their host name strings string-compare equal; or</li>
+ * <li>A is a HDN string and has the form NB, where N is a non-empty
+ * name string, B has the form .B', and B' is a HDN string. (So,
+ * x.y.com domain-matches .Y.com but not Y.com.)</li>
+ * </ul></blockquote>
+ *
+ * <p>A host isn't in a domain (RFC 2965 sec. 3.3.2) if:
+ * <blockquote><ul>
+ * <li>The value for the Domain attribute contains no embedded dots,
+ * and the value is not .local.</li>
+ * <li>The effective host name that derives from the request-host does
+ * not domain-match the Domain attribute.</li>
+ * <li>The request-host is a HDN (not IP address) and has the form HD,
+ * where D is the value of the Domain attribute, and H is a string
+ * that contains one or more dots.</li>
+ * </ul></blockquote>
+ *
+ * <p>Examples:
+ * <blockquote><ul>
+ * <li>A Set-Cookie2 from request-host y.x.foo.com for Domain=.foo.com
+ * would be rejected, because H is y.x and contains a dot.</li>
+ * <li>A Set-Cookie2 from request-host x.foo.com for Domain=.foo.com
+ * would be accepted.</li>
+ * <li>A Set-Cookie2 with Domain=.com or Domain=.com., will always be
+ * rejected, because there is no embedded dot.</li>
+ * <li>A Set-Cookie2 from request-host example for Domain=.local will
+ * be accepted, because the effective host name for the request-
+ * host is example.local, and example.local domain-matches .local.</li>
+ * </ul></blockquote>
+ *
+ * @param domain
+ * the domain name to check host name with
+ *
+ * @param host
+ * the host name in question
+ *
+ * @return {@code true} if they domain-matches; {@code false} if not
+ */
+ public static boolean domainMatches(String domain, String host) {
+ if (domain == null || host == null)
+ return false;
+
+ // if there's no embedded dot in domain and domain is not .local
+ boolean isLocalDomain = ".local".equalsIgnoreCase(domain);
+ int embeddedDotInDomain = domain.indexOf('.');
+ if (embeddedDotInDomain == 0)
+ embeddedDotInDomain = domain.indexOf('.', 1);
+ if (!isLocalDomain
+ && (embeddedDotInDomain == -1 ||
+ embeddedDotInDomain == domain.length() - 1))
+ return false;
+
+ // if the host name contains no dot and the domain name
+ // is .local or host.local
+ int firstDotInHost = host.indexOf('.');
+ if (firstDotInHost == -1 &&
+ (isLocalDomain ||
+ domain.equalsIgnoreCase(host + ".local"))) {
+ return true;
+ }
+
+ int domainLength = domain.length();
+ int lengthDiff = host.length() - domainLength;
+ if (lengthDiff == 0) {
+ // if the host name and the domain name are just string-compare euqal
+ return host.equalsIgnoreCase(domain);
+ }
+ else if (lengthDiff > 0) {
+ // need to check H & D component
+ String H = host.substring(0, lengthDiff);
+ String D = host.substring(lengthDiff);
+
+ // BEGIN Android-changed: App compat reason
+ // 1) Disregard RFC 2965 sec. 3.3.2, the "The request-host is a HDN..."
+ // 2) match "foo.local" for domain ".local".
+ // return (H.indexOf('.') == -1 && D.equalsIgnoreCase(domain));
+ return D.equalsIgnoreCase(domain) && ((domain.startsWith(".") && isFullyQualifiedDomainName(domain, 1))
+ || isLocalDomain);
+ // END Android-changed: App compat reason
+ }
+ else if (lengthDiff == -1) {
+ // if domain is actually .host
+ return (domain.charAt(0) == '.' &&
+ host.equalsIgnoreCase(domain.substring(1)));
+ }
+
+ return false;
+ }
+
+ // BEGIN Android-added: App compat reason
+ private static boolean isFullyQualifiedDomainName(String s, int firstCharacter) {
+ int dotPosition = s.indexOf('.', firstCharacter + 1);
+ return dotPosition != -1 && dotPosition < s.length() - 1;
+ }
+ // END Android-added: App compat reason
+
+ /**
+ * Constructs a cookie header string representation of this cookie,
+ * which is in the format defined by corresponding cookie specification,
+ * but without the leading "Cookie:" token.
+ *
+ * @return a string form of the cookie. The string has the defined format
+ */
+ @Override
+ public String toString() {
+ if (getVersion() > 0) {
+ return toRFC2965HeaderString();
+ } else {
+ return toNetscapeHeaderString();
+ }
+ }
+
+ /**
+ * Test the equality of two HTTP cookies.
+ *
+ * <p> The result is {@code true} only if two cookies come from same domain
+ * (case-insensitive), have same name (case-insensitive), and have same path
+ * (case-sensitive).
+ *
+ * @return {@code true} if two HTTP cookies equal to each other;
+ * otherwise, {@code false}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ if (!(obj instanceof HttpCookie))
+ return false;
+ HttpCookie other = (HttpCookie)obj;
+
+ // One http cookie equals to another cookie (RFC 2965 sec. 3.3.3) if:
+ // 1. they come from same domain (case-insensitive),
+ // 2. have same name (case-insensitive),
+ // 3. and have same path (case-sensitive).
+ return equalsIgnoreCase(getName(), other.getName()) &&
+ equalsIgnoreCase(getDomain(), other.getDomain()) &&
+ Objects.equals(getPath(), other.getPath());
+ }
+
+ /**
+ * Returns the hash code of this HTTP cookie. The result is the sum of
+ * hash code value of three significant components of this cookie: name,
+ * domain, and path. That is, the hash code is the value of the expression:
+ * <blockquote>
+ * getName().toLowerCase().hashCode()<br>
+ * + getDomain().toLowerCase().hashCode()<br>
+ * + getPath().hashCode()
+ * </blockquote>
+ *
+ * @return this HTTP cookie's hash code
+ */
+ @Override
+ public int hashCode() {
+ int h1 = name.toLowerCase().hashCode();
+ int h2 = (domain!=null) ? domain.toLowerCase().hashCode() : 0;
+ int h3 = (path!=null) ? path.hashCode() : 0;
+
+ return h1 + h2 + h3;
+ }
+
+ /**
+ * Create and return a copy of this object.
+ *
+ * @return a clone of this HTTP cookie
+ */
+ @Override
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ // ---------------- Private operations --------------
+
+ // Note -- disabled for now to allow full Netscape compatibility
+ // from RFC 2068, token special case characters
+ //
+ // private static final String tspecials = "()<>@,;:\\\"/[]?={} \t";
+ // Android-changed: App compat reason. Disallow "=\t" as token
+ // private static final String tspecials = ",; "; // deliberately includes space
+ private static final String tspecials = ",;= \t";
+
+ /*
+ * Tests a string and returns true if the string counts as a token.
+ *
+ * @param value
+ * the {@code String} to be tested
+ *
+ * @return {@code true} if the {@code String} is a token;
+ * {@code false} if it is not
+ */
+ private static boolean isToken(String value) {
+ // Android-added: Reserved name can't be a token
+ if (RESERVED_NAMES.contains(value.toLowerCase(Locale.US))) {
+ return false;
+ }
+
+ int len = value.length();
+
+ for (int i = 0; i < len; i++) {
+ char c = value.charAt(i);
+
+ if (c < 0x20 || c >= 0x7f || tspecials.indexOf(c) != -1)
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * Parse header string to cookie object.
+ *
+ * @param header
+ * header string; should contain only one NAME=VALUE pair
+ *
+ * @return an HttpCookie being extracted
+ *
+ * @throws IllegalArgumentException
+ * if header string violates the cookie specification
+ */
+ private static HttpCookie parseInternal(String header,
+ boolean retainHeader)
+ {
+ HttpCookie cookie = null;
+ String namevaluePair = null;
+
+ StringTokenizer tokenizer = new StringTokenizer(header, ";");
+
+ // there should always have at least on name-value pair;
+ // it's cookie's name
+ try {
+ namevaluePair = tokenizer.nextToken();
+ int index = namevaluePair.indexOf('=');
+ if (index != -1) {
+ String name = namevaluePair.substring(0, index).trim();
+ String value = namevaluePair.substring(index + 1).trim();
+ if (retainHeader)
+ cookie = new HttpCookie(name,
+ stripOffSurroundingQuote(value),
+ header);
+ else
+ cookie = new HttpCookie(name,
+ stripOffSurroundingQuote(value));
+ } else {
+ // no "=" in name-value pair; it's an error
+ throw new IllegalArgumentException("Invalid cookie name-value pair");
+ }
+ } catch (NoSuchElementException ignored) {
+ throw new IllegalArgumentException("Empty cookie header string");
+ }
+
+ // remaining name-value pairs are cookie's attributes
+ while (tokenizer.hasMoreTokens()) {
+ namevaluePair = tokenizer.nextToken();
+ int index = namevaluePair.indexOf('=');
+ String name, value;
+ if (index != -1) {
+ name = namevaluePair.substring(0, index).trim();
+ value = namevaluePair.substring(index + 1).trim();
+ } else {
+ name = namevaluePair.trim();
+ value = null;
+ }
+
+ // assign attribute to cookie
+ assignAttribute(cookie, name, value);
+ }
+
+ return cookie;
+ }
+
+ /*
+ * assign cookie attribute value to attribute name;
+ * use a map to simulate method dispatch
+ */
+ static interface CookieAttributeAssignor {
+ public void assign(HttpCookie cookie,
+ String attrName,
+ String attrValue);
+ }
+ static final java.util.Map<String, CookieAttributeAssignor> assignors =
+ new java.util.HashMap<>();
+ static {
+ assignors.put("comment", new CookieAttributeAssignor() {
+ public void assign(HttpCookie cookie,
+ String attrName,
+ String attrValue) {
+ if (cookie.getComment() == null)
+ cookie.setComment(attrValue);
+ }
+ });
+ assignors.put("commenturl", new CookieAttributeAssignor() {
+ public void assign(HttpCookie cookie,
+ String attrName,
+ String attrValue) {
+ if (cookie.getCommentURL() == null)
+ cookie.setCommentURL(attrValue);
+ }
+ });
+ assignors.put("discard", new CookieAttributeAssignor() {
+ public void assign(HttpCookie cookie,
+ String attrName,
+ String attrValue) {
+ cookie.setDiscard(true);
+ }
+ });
+ assignors.put("domain", new CookieAttributeAssignor(){
+ public void assign(HttpCookie cookie,
+ String attrName,
+ String attrValue) {
+ if (cookie.getDomain() == null)
+ cookie.setDomain(attrValue);
+ }
+ });
+ assignors.put("max-age", new CookieAttributeAssignor(){
+ public void assign(HttpCookie cookie,
+ String attrName,
+ String attrValue) {
+ try {
+ long maxage = Long.parseLong(attrValue);
+ if (cookie.getMaxAge() == MAX_AGE_UNSPECIFIED)
+ cookie.setMaxAge(maxage);
+ } catch (NumberFormatException ignored) {
+ throw new IllegalArgumentException(
+ "Illegal cookie max-age attribute");
+ }
+ }
+ });
+ assignors.put("path", new CookieAttributeAssignor(){
+ public void assign(HttpCookie cookie,
+ String attrName,
+ String attrValue) {
+ if (cookie.getPath() == null)
+ cookie.setPath(attrValue);
+ }
+ });
+ assignors.put("port", new CookieAttributeAssignor(){
+ public void assign(HttpCookie cookie,
+ String attrName,
+ String attrValue) {
+ if (cookie.getPortlist() == null)
+ cookie.setPortlist(attrValue == null ? "" : attrValue);
+ }
+ });
+ assignors.put("secure", new CookieAttributeAssignor(){
+ public void assign(HttpCookie cookie,
+ String attrName,
+ String attrValue) {
+ cookie.setSecure(true);
+ }
+ });
+ assignors.put("httponly", new CookieAttributeAssignor(){
+ public void assign(HttpCookie cookie,
+ String attrName,
+ String attrValue) {
+ cookie.setHttpOnly(true);
+ }
+ });
+ assignors.put("version", new CookieAttributeAssignor(){
+ public void assign(HttpCookie cookie,
+ String attrName,
+ String attrValue) {
+ try {
+ int version = Integer.parseInt(attrValue);
+ cookie.setVersion(version);
+ } catch (NumberFormatException ignored) {
+ // Just ignore bogus version, it will default to 0 or 1
+ }
+ }
+ });
+ assignors.put("expires", new CookieAttributeAssignor(){ // Netscape only
+ public void assign(HttpCookie cookie,
+ String attrName,
+ String attrValue) {
+ if (cookie.getMaxAge() == MAX_AGE_UNSPECIFIED) {
+ // BEGIN Android-changed: Use HttpDate for date parsing.
+ // it accepts broader set of date formats.
+ // cookie.setMaxAge(cookie.expiryDate2DeltaSeconds(attrValue));
+ // Android-changed: Altered max age calculation to avoid setting
+ // it to MAX_AGE_UNSPECIFIED (-1) if "expires" is one second in past.
+ Date date = HttpDate.parse(attrValue);
+ long maxAgeInSeconds = 0;
+ if (date != null) {
+ maxAgeInSeconds = (date.getTime() - cookie.whenCreated) / 1000;
+ // Avoid MAX_AGE_UNSPECIFIED
+ if (maxAgeInSeconds == MAX_AGE_UNSPECIFIED) {
+ maxAgeInSeconds = 0;
+ }
+ }
+ cookie.setMaxAge(maxAgeInSeconds);
+ // END Android-changed: Use HttpDate for date parsing.
+ }
+ }
+ });
+ }
+ private static void assignAttribute(HttpCookie cookie,
+ String attrName,
+ String attrValue)
+ {
+ // strip off the surrounding "-sign if there's any
+ attrValue = stripOffSurroundingQuote(attrValue);
+
+ CookieAttributeAssignor assignor = assignors.get(attrName.toLowerCase());
+ if (assignor != null) {
+ assignor.assign(cookie, attrName, attrValue);
+ } else {
+ // Ignore the attribute as per RFC 2965
+ }
+ }
+
+ // BEGIN Android-removed: Android doesn't use JavaNetHttpCookieAccess
+ /*
+ static {
+ sun.misc.SharedSecrets.setJavaNetHttpCookieAccess(
+ new sun.misc.JavaNetHttpCookieAccess() {
+ public List<HttpCookie> parse(String header) {
+ return HttpCookie.parse(header, true);
+ }
+
+ public String header(HttpCookie cookie) {
+ return cookie.header;
+ }
+ }
+ );
+ }
+ */
+ // END Android-removed: Android doesn't use JavaNetHttpCookieAccess
+
+ /*
+ * Returns the original header this cookie was consructed from, if it was
+ * constructed by parsing a header, otherwise null.
+ */
+ private String header() {
+ return header;
+ }
+
+ /*
+ * Constructs a string representation of this cookie. The string format is
+ * as Netscape spec, but without leading "Cookie:" token.
+ */
+ private String toNetscapeHeaderString() {
+ return getName() + "=" + getValue();
+ }
+
+ /*
+ * Constructs a string representation of this cookie. The string format is
+ * as RFC 2965/2109, but without leading "Cookie:" token.
+ */
+ private String toRFC2965HeaderString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(getName()).append("=\"").append(getValue()).append('"');
+ if (getPath() != null)
+ sb.append(";$Path=\"").append(getPath()).append('"');
+ if (getDomain() != null)
+ sb.append(";$Domain=\"").append(getDomain()).append('"');
+ if (getPortlist() != null)
+ sb.append(";$Port=\"").append(getPortlist()).append('"');
+
+ return sb.toString();
+ }
+
+ static final TimeZone GMT = TimeZone.getTimeZone("GMT");
+
+ // BEGIN Android-removed: not used.
+ /*
+ * @param dateString
+ * a date string in one of the formats defined in Netscape cookie spec
+ *
+ * @return delta seconds between this cookie's creation time and the time
+ * specified by dateString
+ *
+ private long expiryDate2DeltaSeconds(String dateString) {
+ Calendar cal = new GregorianCalendar(GMT);
+ for (int i = 0; i < COOKIE_DATE_FORMATS.length; i++) {
+ SimpleDateFormat df = new SimpleDateFormat(COOKIE_DATE_FORMATS[i],
+ Locale.US);
+ cal.set(1970, 0, 1, 0, 0, 0);
+ df.setTimeZone(GMT);
+ df.setLenient(false);
+ df.set2DigitYearStart(cal.getTime());
+ try {
+ cal.setTime(df.parse(dateString));
+ if (!COOKIE_DATE_FORMATS[i].contains("yyyy")) {
+ // 2-digit years following the standard set
+ // out it rfc 6265
+ int year = cal.get(Calendar.YEAR);
+ year %= 100;
+ if (year < 70) {
+ year += 2000;
+ } else {
+ year += 1900;
+ }
+ cal.set(Calendar.YEAR, year);
+ }
+ return (cal.getTimeInMillis() - whenCreated) / 1000;
+ } catch (Exception e) {
+ // Ignore, try the next date format
+ }
+ }
+ return 0;
+ }
+ */
+ // END Android-removed: not used.
+
+ /*
+ * try to guess the cookie version through set-cookie header string
+ */
+ private static int guessCookieVersion(String header) {
+ int version = 0;
+
+ header = header.toLowerCase();
+ if (header.indexOf("expires=") != -1) {
+ // only netscape cookie using 'expires'
+ version = 0;
+ } else if (header.indexOf("version=") != -1) {
+ // version is mandatory for rfc 2965/2109 cookie
+ version = 1;
+ } else if (header.indexOf("max-age") != -1) {
+ // rfc 2965/2109 use 'max-age'
+ version = 1;
+ } else if (startsWithIgnoreCase(header, SET_COOKIE2)) {
+ // only rfc 2965 cookie starts with 'set-cookie2'
+ version = 1;
+ }
+
+ return version;
+ }
+
+ private static String stripOffSurroundingQuote(String str) {
+ if (str != null && str.length() > 2 &&
+ str.charAt(0) == '"' && str.charAt(str.length() - 1) == '"') {
+ return str.substring(1, str.length() - 1);
+ }
+ if (str != null && str.length() > 2 &&
+ str.charAt(0) == '\'' && str.charAt(str.length() - 1) == '\'') {
+ return str.substring(1, str.length() - 1);
+ }
+ return str;
+ }
+
+ private static boolean equalsIgnoreCase(String s, String t) {
+ if (s == t) return true;
+ if ((s != null) && (t != null)) {
+ return s.equalsIgnoreCase(t);
+ }
+ return false;
+ }
+
+ private static boolean startsWithIgnoreCase(String s, String start) {
+ if (s == null || start == null) return false;
+
+ if (s.length() >= start.length() &&
+ start.equalsIgnoreCase(s.substring(0, start.length()))) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /*
+ * Split cookie header string according to rfc 2965:
+ * 1) split where it is a comma;
+ * 2) but not the comma surrounding by double-quotes, which is the comma
+ * inside port list or embeded URIs.
+ *
+ * @param header
+ * the cookie header string to split
+ *
+ * @return list of strings; never null
+ */
+ private static List<String> splitMultiCookies(String header) {
+ List<String> cookies = new java.util.ArrayList<String>();
+ int quoteCount = 0;
+ int p, q;
+
+ for (p = 0, q = 0; p < header.length(); p++) {
+ char c = header.charAt(p);
+ if (c == '"') quoteCount++;
+ if (c == ',' && (quoteCount % 2 == 0)) {
+ // it is comma and not surrounding by double-quotes
+ cookies.add(header.substring(q, p));
+ q = p + 1;
+ }
+ }
+
+ cookies.add(header.substring(q));
+
+ return cookies;
+ }
+}
diff --git a/java/net/HttpRetryException.java b/java/net/HttpRetryException.java
new file mode 100644
index 0000000..d498a65
--- /dev/null
+++ b/java/net/HttpRetryException.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+
+/**
+ * Thrown to indicate that a HTTP request needs to be retried
+ * but cannot be retried automatically, due to streaming mode
+ * being enabled.
+ *
+ * @author Michael McMahon
+ * @since 1.5
+ */
+public
+class HttpRetryException extends IOException {
+ private static final long serialVersionUID = -9186022286469111381L;
+
+ private int responseCode;
+ private String location;
+
+ /**
+ * Constructs a new {@code HttpRetryException} from the
+ * specified response code and exception detail message
+ *
+ * @param detail the detail message.
+ * @param code the HTTP response code from server.
+ */
+ public HttpRetryException(String detail, int code) {
+ super(detail);
+ responseCode = code;
+ }
+
+ /**
+ * Constructs a new {@code HttpRetryException} with detail message
+ * responseCode and the contents of the Location response header field.
+ *
+ * @param detail the detail message.
+ * @param code the HTTP response code from server.
+ * @param location the URL to be redirected to
+ */
+ public HttpRetryException(String detail, int code, String location) {
+ super (detail);
+ responseCode = code;
+ this.location = location;
+ }
+
+ /**
+ * Returns the http response code
+ *
+ * @return The http response code.
+ */
+ public int responseCode() {
+ return responseCode;
+ }
+
+ /**
+ * Returns a string explaining why the http request could
+ * not be retried.
+ *
+ * @return The reason string
+ */
+ public String getReason() {
+ return super.getMessage();
+ }
+
+ /**
+ * Returns the value of the Location header field if the
+ * error resulted from redirection.
+ *
+ * @return The location string
+ */
+ public String getLocation() {
+ return location;
+ }
+}
diff --git a/java/net/HttpURLConnection.java b/java/net/HttpURLConnection.java
new file mode 100644
index 0000000..b72168b
--- /dev/null
+++ b/java/net/HttpURLConnection.java
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.security.Permission;
+import java.util.Date;
+
+// Android-changed: top-level documentation substantially changed/rewritten.
+/**
+ * A URLConnection with support for HTTP-specific features. See
+ * <A HREF="http://www.w3.org/pub/WWW/Protocols/"> the spec </A> for
+ * details.
+ * <p>
+ *
+ * <p>Uses of this class follow a pattern:
+ * <ol>
+ * <li>Obtain a new {@code HttpURLConnection} by calling {@link
+ * URL#openConnection() URL.openConnection()} and casting the result to
+ * {@code HttpURLConnection}.
+ * <li>Prepare the request. The primary property of a request is its URI.
+ * Request headers may also include metadata such as credentials, preferred
+ * content types, and session cookies.
+ * <li>Optionally upload a request body. Instances must be configured with
+ * {@link #setDoOutput(boolean) setDoOutput(true)} if they include a
+ * request body. Transmit data by writing to the stream returned by {@link
+ * #getOutputStream()}.
+ * <li>Read the response. Response headers typically include metadata such as
+ * the response body's content type and length, modified dates and session
+ * cookies. The response body may be read from the stream returned by {@link
+ * #getInputStream()}. If the response has no body, that method returns an
+ * empty stream.
+ * <li>Disconnect. Once the response body has been read, the {@code
+ * HttpURLConnection} should be closed by calling {@link #disconnect()}.
+ * Disconnecting releases the resources held by a connection so they may
+ * be closed or reused.
+ * </ol>
+ *
+ * <p>For example, to retrieve the webpage at {@code http://www.android.com/}:
+ * <pre> {@code
+ * URL url = new URL("http://www.android.com/");
+ * HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
+ * try {
+ * InputStream in = new BufferedInputStream(urlConnection.getInputStream());
+ * readStream(in);
+ * } finally {
+ * urlConnection.disconnect();
+ * }
+ * }</pre>
+ *
+ * <h3>Secure Communication with HTTPS</h3>
+ * Calling {@link URL#openConnection()} on a URL with the "https"
+ * scheme will return an {@code HttpsURLConnection}, which allows for
+ * overriding the default {@link javax.net.ssl.HostnameVerifier
+ * HostnameVerifier} and {@link javax.net.ssl.SSLSocketFactory
+ * SSLSocketFactory}. An application-supplied {@code SSLSocketFactory}
+ * created from an {@link javax.net.ssl.SSLContext SSLContext} can
+ * provide a custom {@link javax.net.ssl.X509TrustManager
+ * X509TrustManager} for verifying certificate chains and a custom
+ * {@link javax.net.ssl.X509KeyManager X509KeyManager} for supplying
+ * client certificates. See {@link javax.net.ssl.HttpsURLConnection
+ * HttpsURLConnection} for more details.
+ *
+ * <h3>Response Handling</h3>
+ * {@code HttpURLConnection} will follow up to five HTTP redirects. It will
+ * follow redirects from one origin server to another. This implementation
+ * doesn't follow redirects from HTTPS to HTTP or vice versa.
+ *
+ * <p>If the HTTP response indicates that an error occurred, {@link
+ * #getInputStream()} will throw an {@link IOException}. Use {@link
+ * #getErrorStream()} to read the error response. The headers can be read in
+ * the normal way using {@link #getHeaderFields()},
+ *
+ * <h3>Posting Content</h3>
+ * To upload data to a web server, configure the connection for output using
+ * {@link #setDoOutput(boolean) setDoOutput(true)}.
+ *
+ * <p>For best performance, you should call either {@link
+ * #setFixedLengthStreamingMode(int)} when the body length is known in advance,
+ * or {@link #setChunkedStreamingMode(int)} when it is not. Otherwise {@code
+ * HttpURLConnection} will be forced to buffer the complete request body in
+ * memory before it is transmitted, wasting (and possibly exhausting) heap and
+ * increasing latency.
+ *
+ * <p>For example, to perform an upload: <pre> {@code
+ * HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
+ * try {
+ * urlConnection.setDoOutput(true);
+ * urlConnection.setChunkedStreamingMode(0);
+ *
+ * OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
+ * writeStream(out);
+ *
+ * InputStream in = new BufferedInputStream(urlConnection.getInputStream());
+ * readStream(in);
+ * } finally {
+ * urlConnection.disconnect();
+ * }
+ * }</pre>
+ *
+ * <h3>Performance</h3>
+ * The input and output streams returned by this class are <strong>not
+ * buffered</strong>. Most callers should wrap the returned streams with {@link
+ * java.io.BufferedInputStream BufferedInputStream} or {@link
+ * java.io.BufferedOutputStream BufferedOutputStream}. Callers that do only bulk
+ * reads or writes may omit buffering.
+ *
+ * <p>When transferring large amounts of data to or from a server, use streams
+ * to limit how much data is in memory at once. Unless you need the entire
+ * body to be in memory at once, process it as a stream (rather than storing
+ * the complete body as a single byte array or string).
+ *
+ * <p>To reduce latency, this class may reuse the same underlying {@code Socket}
+ * for multiple request/response pairs. As a result, HTTP connections may be
+ * held open longer than necessary. Calls to {@link #disconnect()} may return
+ * the socket to a pool of connected sockets.
+ *
+ * <p>By default, this implementation of {@code HttpURLConnection} requests that
+ * servers use gzip compression and it automatically decompresses the data for
+ * callers of {@link #getInputStream()}. The Content-Encoding and Content-Length
+ * response headers are cleared in this case. Gzip compression can be disabled by
+ * setting the acceptable encodings in the request header: <pre> {@code
+ * urlConnection.setRequestProperty("Accept-Encoding", "identity");
+ * }</pre>
+ *
+ * <p>Setting the Accept-Encoding request header explicitly disables automatic
+ * decompression and leaves the response headers intact; callers must handle
+ * decompression as needed, according to the Content-Encoding header of the
+ * response.
+ *
+ * <p>{@link #getContentLength()} returns the number of bytes transmitted and
+ * cannot be used to predict how many bytes can be read from
+ * {@link #getInputStream()} for compressed streams. Instead, read that stream
+ * until it is exhausted, i.e. when {@link InputStream#read} returns -1.
+ *
+ * <h3>Handling Network Sign-On</h3>
+ * Some Wi-Fi networks block Internet access until the user clicks through a
+ * sign-on page. Such sign-on pages are typically presented by using HTTP
+ * redirects. You can use {@link #getURL()} to test if your connection has been
+ * unexpectedly redirected. This check is not valid until <strong>after</strong>
+ * the response headers have been received, which you can trigger by calling
+ * {@link #getHeaderFields()} or {@link #getInputStream()}. For example, to
+ * check that a response was not redirected to an unexpected host:
+ * <pre> {@code
+ * HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
+ * try {
+ * InputStream in = new BufferedInputStream(urlConnection.getInputStream());
+ * if (!url.getHost().equals(urlConnection.getURL().getHost())) {
+ * // we were redirected! Kick the user out to the browser to sign on?
+ * }
+ * ...
+ * } finally {
+ * urlConnection.disconnect();
+ * }
+ * }</pre>
+ *
+ * <h3>HTTP Authentication</h3>
+ * {@code HttpURLConnection} supports <a
+ * href="http://www.ietf.org/rfc/rfc2617">HTTP basic authentication</a>. Use
+ * {@link Authenticator} to set the VM-wide authentication handler:
+ * <pre> {@code
+ * Authenticator.setDefault(new Authenticator() {
+ * protected PasswordAuthentication getPasswordAuthentication() {
+ * return new PasswordAuthentication(username, password.toCharArray());
+ * }
+ * });
+ * }</pre>
+ * Unless paired with HTTPS, this is <strong>not</strong> a secure mechanism for
+ * user authentication. In particular, the username, password, request and
+ * response are all transmitted over the network without encryption.
+ *
+ * <h3>Sessions with Cookies</h3>
+ * To establish and maintain a potentially long-lived session between client
+ * and server, {@code HttpURLConnection} includes an extensible cookie manager.
+ * Enable VM-wide cookie management using {@link CookieHandler} and {@link
+ * CookieManager}: <pre> {@code
+ * CookieManager cookieManager = new CookieManager();
+ * CookieHandler.setDefault(cookieManager);
+ * }</pre>
+ * By default, {@code CookieManager} accepts cookies from the <a
+ * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec1.html">origin
+ * server</a> only. Two other policies are included: {@link
+ * CookiePolicy#ACCEPT_ALL} and {@link CookiePolicy#ACCEPT_NONE}. Implement
+ * {@link CookiePolicy} to define a custom policy.
+ *
+ * <p>The default {@code CookieManager} keeps all accepted cookies in memory. It
+ * will forget these cookies when the VM exits. Implement {@link CookieStore} to
+ * define a custom cookie store.
+ *
+ * <p>In addition to the cookies set by HTTP responses, you may set cookies
+ * programmatically. To be included in HTTP request headers, cookies must have
+ * the domain and path properties set.
+ *
+ * <p>By default, new instances of {@code HttpCookie} work only with servers
+ * that support <a href="http://www.ietf.org/rfc/rfc2965.txt">RFC 2965</a>
+ * cookies. Many web servers support only the older specification, <a
+ * href="http://www.ietf.org/rfc/rfc2109.txt">RFC 2109</a>. For compatibility
+ * with the most web servers, set the cookie version to 0.
+ *
+ * <p>For example, to receive {@code www.twitter.com} in French: <pre> {@code
+ * HttpCookie cookie = new HttpCookie("lang", "fr");
+ * cookie.setDomain("twitter.com");
+ * cookie.setPath("/");
+ * cookie.setVersion(0);
+ * cookieManager.getCookieStore().add(new URI("http://twitter.com/"), cookie);
+ * }</pre>
+ *
+ * <h3>HTTP Methods</h3>
+ * <p>{@code HttpURLConnection} uses the {@code GET} method by default. It will
+ * use {@code POST} if {@link #setDoOutput setDoOutput(true)} has been called.
+ * Other HTTP methods ({@code OPTIONS}, {@code HEAD}, {@code PUT}, {@code
+ * DELETE} and {@code TRACE}) can be used with {@link #setRequestMethod}.
+ *
+ * <h3>Proxies</h3>
+ * By default, this class will connect directly to the <a
+ * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec1.html">origin
+ * server</a>. It can also connect via an {@link Proxy.Type#HTTP HTTP} or {@link
+ * Proxy.Type#SOCKS SOCKS} proxy. To use a proxy, use {@link
+ * URL#openConnection(Proxy) URL.openConnection(Proxy)} when creating the
+ * connection.
+ *
+ * <h3>IPv6 Support</h3>
+ * <p>This class includes transparent support for IPv6. For hosts with both IPv4
+ * and IPv6 addresses, it will attempt to connect to each of a host's addresses
+ * until a connection is established.
+ *
+ * <h3>Response Caching</h3>
+ * Android 4.0 (Ice Cream Sandwich, API level 15) includes a response cache. See
+ * {@code android.net.http.HttpResponseCache} for instructions on enabling HTTP
+ * caching in your application.
+ *
+ * <h3>Avoiding Bugs In Earlier Releases</h3>
+ * Prior to Android 2.2 (Froyo), this class had some frustrating bugs. In
+ * particular, calling {@code close()} on a readable {@code InputStream} could
+ * <a href="http://code.google.com/p/android/issues/detail?id=2939">poison the
+ * connection pool</a>. Work around this by disabling connection pooling:
+ * <pre> {@code
+ * private void disableConnectionReuseIfNecessary() {
+ * // Work around pre-Froyo bugs in HTTP connection reuse.
+ * if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
+ * System.setProperty("http.keepAlive", "false");
+ * }
+ * }}</pre>
+ *
+ * <p>Each instance of {@code HttpURLConnection} may be used for one
+ * request/response pair. Instances of this class are not thread safe.
+ *
+ * @see java.net.HttpURLConnection#disconnect()
+ * @since JDK1.1
+ */
+abstract public class HttpURLConnection extends URLConnection {
+ /* instance variables */
+
+ /**
+ * The HTTP method (GET,POST,PUT,etc.).
+ */
+ protected String method = "GET";
+
+ /**
+ * The chunk-length when using chunked encoding streaming mode for output.
+ * A value of {@code -1} means chunked encoding is disabled for output.
+ * @since 1.5
+ */
+ protected int chunkLength = -1;
+
+ /**
+ * The fixed content-length when using fixed-length streaming mode.
+ * A value of {@code -1} means fixed-length streaming mode is disabled
+ * for output.
+ *
+ * <P> <B>NOTE:</B> {@link #fixedContentLengthLong} is recommended instead
+ * of this field, as it allows larger content lengths to be set.
+ *
+ * @since 1.5
+ */
+ protected int fixedContentLength = -1;
+
+ /**
+ * The fixed content-length when using fixed-length streaming mode.
+ * A value of {@code -1} means fixed-length streaming mode is disabled
+ * for output.
+ *
+ * @since 1.7
+ */
+ protected long fixedContentLengthLong = -1;
+
+ /**
+ * Returns the key for the {@code n}<sup>th</sup> header field.
+ * Some implementations may treat the {@code 0}<sup>th</sup>
+ * header field as special, i.e. as the status line returned by the HTTP
+ * server. In this case, {@link #getHeaderField(int) getHeaderField(0)} returns the status
+ * line, but {@code getHeaderFieldKey(0)} returns null.
+ *
+ * @param n an index, where {@code n >=0}.
+ * @return the key for the {@code n}<sup>th</sup> header field,
+ * or {@code null} if the key does not exist.
+ */
+ public String getHeaderFieldKey (int n) {
+ return null;
+ }
+
+ /**
+ * This method is used to enable streaming of a HTTP request body
+ * without internal buffering, when the content length is known in
+ * advance.
+ * <p>
+ * An exception will be thrown if the application
+ * attempts to write more data than the indicated
+ * content-length, or if the application closes the OutputStream
+ * before writing the indicated amount.
+ * <p>
+ * When output streaming is enabled, authentication
+ * and redirection cannot be handled automatically.
+ * A HttpRetryException will be thrown when reading
+ * the response if authentication or redirection are required.
+ * This exception can be queried for the details of the error.
+ * <p>
+ * This method must be called before the URLConnection is connected.
+ * <p>
+ * <B>NOTE:</B> {@link #setFixedLengthStreamingMode(long)} is recommended
+ * instead of this method as it allows larger content lengths to be set.
+ *
+ * @param contentLength The number of bytes which will be written
+ * to the OutputStream.
+ *
+ * @throws IllegalStateException if URLConnection is already connected
+ * or if a different streaming mode is already enabled.
+ *
+ * @throws IllegalArgumentException if a content length less than
+ * zero is specified.
+ *
+ * @see #setChunkedStreamingMode(int)
+ * @since 1.5
+ */
+ public void setFixedLengthStreamingMode (int contentLength) {
+ if (connected) {
+ throw new IllegalStateException ("Already connected");
+ }
+ if (chunkLength != -1) {
+ throw new IllegalStateException ("Chunked encoding streaming mode set");
+ }
+ if (contentLength < 0) {
+ throw new IllegalArgumentException ("invalid content length");
+ }
+ fixedContentLength = contentLength;
+ }
+
+ /**
+ * This method is used to enable streaming of a HTTP request body
+ * without internal buffering, when the content length is known in
+ * advance.
+ *
+ * <P> An exception will be thrown if the application attempts to write
+ * more data than the indicated content-length, or if the application
+ * closes the OutputStream before writing the indicated amount.
+ *
+ * <P> When output streaming is enabled, authentication and redirection
+ * cannot be handled automatically. A {@linkplain HttpRetryException} will
+ * be thrown when reading the response if authentication or redirection
+ * are required. This exception can be queried for the details of the
+ * error.
+ *
+ * <P> This method must be called before the URLConnection is connected.
+ *
+ * <P> The content length set by invoking this method takes precedence
+ * over any value set by {@link #setFixedLengthStreamingMode(int)}.
+ *
+ * @param contentLength
+ * The number of bytes which will be written to the OutputStream.
+ *
+ * @throws IllegalStateException
+ * if URLConnection is already connected or if a different
+ * streaming mode is already enabled.
+ *
+ * @throws IllegalArgumentException
+ * if a content length less than zero is specified.
+ *
+ * @since 1.7
+ */
+ public void setFixedLengthStreamingMode(long contentLength) {
+ if (connected) {
+ throw new IllegalStateException("Already connected");
+ }
+ if (chunkLength != -1) {
+ throw new IllegalStateException(
+ "Chunked encoding streaming mode set");
+ }
+ if (contentLength < 0) {
+ throw new IllegalArgumentException("invalid content length");
+ }
+ fixedContentLengthLong = contentLength;
+ }
+
+ /* Default chunk size (including chunk header) if not specified;
+ * we want to keep this in sync with the one defined in
+ * sun.net.www.http.ChunkedOutputStream
+ */
+ private static final int DEFAULT_CHUNK_SIZE = 4096;
+
+ /**
+ * This method is used to enable streaming of a HTTP request body
+ * without internal buffering, when the content length is <b>not</b>
+ * known in advance. In this mode, chunked transfer encoding
+ * is used to send the request body. Note, not all HTTP servers
+ * support this mode.
+ * <p>
+ * When output streaming is enabled, authentication
+ * and redirection cannot be handled automatically.
+ * A HttpRetryException will be thrown when reading
+ * the response if authentication or redirection are required.
+ * This exception can be queried for the details of the error.
+ * <p>
+ * This method must be called before the URLConnection is connected.
+ *
+ * @param chunklen The number of bytes to write in each chunk.
+ * If chunklen is less than or equal to zero, a default
+ * value will be used.
+ *
+ * @throws IllegalStateException if URLConnection is already connected
+ * or if a different streaming mode is already enabled.
+ *
+ * @see #setFixedLengthStreamingMode(int)
+ * @since 1.5
+ */
+ public void setChunkedStreamingMode (int chunklen) {
+ if (connected) {
+ throw new IllegalStateException ("Can't set streaming mode: already connected");
+ }
+ if (fixedContentLength != -1 || fixedContentLengthLong != -1) {
+ throw new IllegalStateException ("Fixed length streaming mode set");
+ }
+ chunkLength = chunklen <=0? DEFAULT_CHUNK_SIZE : chunklen;
+ }
+
+ /**
+ * Returns the value for the {@code n}<sup>th</sup> header field.
+ * Some implementations may treat the {@code 0}<sup>th</sup>
+ * header field as special, i.e. as the status line returned by the HTTP
+ * server.
+ * <p>
+ * This method can be used in conjunction with the
+ * {@link #getHeaderFieldKey getHeaderFieldKey} method to iterate through all
+ * the headers in the message.
+ *
+ * @param n an index, where {@code n>=0}.
+ * @return the value of the {@code n}<sup>th</sup> header field,
+ * or {@code null} if the value does not exist.
+ * @see java.net.HttpURLConnection#getHeaderFieldKey(int)
+ */
+ public String getHeaderField(int n) {
+ return null;
+ }
+
+ /**
+ * An {@code int} representing the three digit HTTP Status-Code.
+ * <ul>
+ * <li> 1xx: Informational
+ * <li> 2xx: Success
+ * <li> 3xx: Redirection
+ * <li> 4xx: Client Error
+ * <li> 5xx: Server Error
+ * </ul>
+ */
+ protected int responseCode = -1;
+
+ /**
+ * The HTTP response message.
+ */
+ protected String responseMessage = null;
+
+ /* static variables */
+
+ /* do we automatically follow redirects? The default is true. */
+ private static boolean followRedirects = true;
+
+ /**
+ * If {@code true}, the protocol will automatically follow redirects.
+ * If {@code false}, the protocol will not automatically follow
+ * redirects.
+ * <p>
+ * This field is set by the {@code setInstanceFollowRedirects}
+ * method. Its value is returned by the {@code getInstanceFollowRedirects}
+ * method.
+ * <p>
+ * Its default value is based on the value of the static followRedirects
+ * at HttpURLConnection construction time.
+ *
+ * @see java.net.HttpURLConnection#setInstanceFollowRedirects(boolean)
+ * @see java.net.HttpURLConnection#getInstanceFollowRedirects()
+ * @see java.net.HttpURLConnection#setFollowRedirects(boolean)
+ */
+ protected boolean instanceFollowRedirects = followRedirects;
+
+ /* valid HTTP methods */
+ private static final String[] methods = {
+ "GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
+ };
+
+ /**
+ * Constructor for the HttpURLConnection.
+ * @param u the URL
+ */
+ protected HttpURLConnection (URL u) {
+ super(u);
+ }
+
+ /**
+ * Sets whether HTTP redirects (requests with response code 3xx) should
+ * be automatically followed by this class. True by default. Applets
+ * cannot change this variable.
+ * <p>
+ * If there is a security manager, this method first calls
+ * the security manager's {@code checkSetFactory} method
+ * to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ *
+ * @param set a {@code boolean} indicating whether or not
+ * to follow HTTP redirects.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkSetFactory} method doesn't
+ * allow the operation.
+ * @see SecurityManager#checkSetFactory
+ * @see #getFollowRedirects()
+ */
+ public static void setFollowRedirects(boolean set) {
+ SecurityManager sec = System.getSecurityManager();
+ if (sec != null) {
+ // seems to be the best check here...
+ sec.checkSetFactory();
+ }
+ followRedirects = set;
+ }
+
+ /**
+ * Returns a {@code boolean} indicating
+ * whether or not HTTP redirects (3xx) should
+ * be automatically followed.
+ *
+ * @return {@code true} if HTTP redirects should
+ * be automatically followed, {@code false} if not.
+ * @see #setFollowRedirects(boolean)
+ */
+ public static boolean getFollowRedirects() {
+ return followRedirects;
+ }
+
+ /**
+ * Sets whether HTTP redirects (requests with response code 3xx) should
+ * be automatically followed by this {@code HttpURLConnection}
+ * instance.
+ * <p>
+ * The default value comes from followRedirects, which defaults to
+ * true.
+ *
+ * @param followRedirects a {@code boolean} indicating
+ * whether or not to follow HTTP redirects.
+ *
+ * @see java.net.HttpURLConnection#instanceFollowRedirects
+ * @see #getInstanceFollowRedirects
+ * @since 1.3
+ */
+ public void setInstanceFollowRedirects(boolean followRedirects) {
+ instanceFollowRedirects = followRedirects;
+ }
+
+ /**
+ * Returns the value of this {@code HttpURLConnection}'s
+ * {@code instanceFollowRedirects} field.
+ *
+ * @return the value of this {@code HttpURLConnection}'s
+ * {@code instanceFollowRedirects} field.
+ * @see java.net.HttpURLConnection#instanceFollowRedirects
+ * @see #setInstanceFollowRedirects(boolean)
+ * @since 1.3
+ */
+ public boolean getInstanceFollowRedirects() {
+ return instanceFollowRedirects;
+ }
+
+ /**
+ * Set the method for the URL request, one of:
+ * <UL>
+ * <LI>GET
+ * <LI>POST
+ * <LI>HEAD
+ * <LI>OPTIONS
+ * <LI>PUT
+ * <LI>DELETE
+ * <LI>TRACE
+ * </UL> are legal, subject to protocol restrictions. The default
+ * method is GET.
+ *
+ * @param method the HTTP method
+ * @exception ProtocolException if the method cannot be reset or if
+ * the requested method isn't valid for HTTP.
+ * @exception SecurityException if a security manager is set and the
+ * method is "TRACE", but the "allowHttpTrace"
+ * NetPermission is not granted.
+ * @see #getRequestMethod()
+ */
+ public void setRequestMethod(String method) throws ProtocolException {
+ if (connected) {
+ throw new ProtocolException("Can't reset method: already connected");
+ }
+ // This restriction will prevent people from using this class to
+ // experiment w/ new HTTP methods using java. But it should
+ // be placed for security - the request String could be
+ // arbitrarily long.
+
+ for (int i = 0; i < methods.length; i++) {
+ if (methods[i].equals(method)) {
+ if (method.equals("TRACE")) {
+ SecurityManager s = System.getSecurityManager();
+ if (s != null) {
+ s.checkPermission(new NetPermission("allowHttpTrace"));
+ }
+ }
+ this.method = method;
+ return;
+ }
+ }
+ throw new ProtocolException("Invalid HTTP method: " + method);
+ }
+
+ /**
+ * Get the request method.
+ * @return the HTTP request method
+ * @see #setRequestMethod(java.lang.String)
+ */
+ public String getRequestMethod() {
+ return method;
+ }
+
+ /**
+ * Gets the status code from an HTTP response message.
+ * For example, in the case of the following status lines:
+ * <PRE>
+ * HTTP/1.0 200 OK
+ * HTTP/1.0 401 Unauthorized
+ * </PRE>
+ * It will return 200 and 401 respectively.
+ * Returns -1 if no code can be discerned
+ * from the response (i.e., the response is not valid HTTP).
+ * @throws IOException if an error occurred connecting to the server.
+ * @return the HTTP Status-Code, or -1
+ */
+ public int getResponseCode() throws IOException {
+ /*
+ * We're got the response code already
+ */
+ if (responseCode != -1) {
+ return responseCode;
+ }
+
+ /*
+ * Ensure that we have connected to the server. Record
+ * exception as we need to re-throw it if there isn't
+ * a status line.
+ */
+ Exception exc = null;
+ try {
+ getInputStream();
+ } catch (Exception e) {
+ exc = e;
+ }
+
+ /*
+ * If we can't a status-line then re-throw any exception
+ * that getInputStream threw.
+ */
+ String statusLine = getHeaderField(0);
+ if (statusLine == null) {
+ if (exc != null) {
+ if (exc instanceof RuntimeException)
+ throw (RuntimeException)exc;
+ else
+ throw (IOException)exc;
+ }
+ return -1;
+ }
+
+ /*
+ * Examine the status-line - should be formatted as per
+ * section 6.1 of RFC 2616 :-
+ *
+ * Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase
+ *
+ * If status line can't be parsed return -1.
+ */
+ if (statusLine.startsWith("HTTP/1.")) {
+ int codePos = statusLine.indexOf(' ');
+ if (codePos > 0) {
+
+ int phrasePos = statusLine.indexOf(' ', codePos+1);
+ if (phrasePos > 0 && phrasePos < statusLine.length()) {
+ responseMessage = statusLine.substring(phrasePos+1);
+ }
+
+ // deviation from RFC 2616 - don't reject status line
+ // if SP Reason-Phrase is not included.
+ if (phrasePos < 0)
+ phrasePos = statusLine.length();
+
+ try {
+ responseCode = Integer.parseInt
+ (statusLine.substring(codePos+1, phrasePos));
+ return responseCode;
+ } catch (NumberFormatException e) { }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Gets the HTTP response message, if any, returned along with the
+ * response code from a server. From responses like:
+ * <PRE>
+ * HTTP/1.0 200 OK
+ * HTTP/1.0 404 Not Found
+ * </PRE>
+ * Extracts the Strings "OK" and "Not Found" respectively.
+ * Returns null if none could be discerned from the responses
+ * (the result was not valid HTTP).
+ * @throws IOException if an error occurred connecting to the server.
+ * @return the HTTP response message, or {@code null}
+ */
+ public String getResponseMessage() throws IOException {
+ getResponseCode();
+ return responseMessage;
+ }
+
+ @SuppressWarnings("deprecation")
+ public long getHeaderFieldDate(String name, long Default) {
+ String dateString = getHeaderField(name);
+ try {
+ if (dateString.indexOf("GMT") == -1) {
+ dateString = dateString+" GMT";
+ }
+ return Date.parse(dateString);
+ } catch (Exception e) {
+ }
+ return Default;
+ }
+
+
+ /**
+ * Indicates that other requests to the server
+ * are unlikely in the near future. Calling disconnect()
+ * should not imply that this HttpURLConnection
+ * instance can be reused for other requests.
+ */
+ public abstract void disconnect();
+
+ /**
+ * Indicates if the connection is going through a proxy.
+ * @return a boolean indicating if the connection is
+ * using a proxy.
+ */
+ public abstract boolean usingProxy();
+
+ /**
+ * Returns a {@link SocketPermission} object representing the
+ * permission necessary to connect to the destination host and port.
+ *
+ * @exception IOException if an error occurs while computing
+ * the permission.
+ *
+ * @return a {@code SocketPermission} object representing the
+ * permission necessary to connect to the destination
+ * host and port.
+ */
+ public Permission getPermission() throws IOException {
+ int port = url.getPort();
+ port = port < 0 ? 80 : port;
+ String host = url.getHost() + ":" + port;
+ Permission permission = new SocketPermission(host, "connect");
+ return permission;
+ }
+
+ /**
+ * Returns the error stream if the connection failed
+ * but the server sent useful data nonetheless. The
+ * typical example is when an HTTP server responds
+ * with a 404, which will cause a FileNotFoundException
+ * to be thrown in connect, but the server sent an HTML
+ * help page with suggestions as to what to do.
+ *
+ * <p>This method will not cause a connection to be initiated. If
+ * the connection was not connected, or if the server did not have
+ * an error while connecting or if the server had an error but
+ * no error data was sent, this method will return null. This is
+ * the default.
+ *
+ * @return an error stream if any, null if there have been no
+ * errors, the connection is not connected or the server sent no
+ * useful data.
+ */
+ public InputStream getErrorStream() {
+ return null;
+ }
+
+ /**
+ * The response codes for HTTP, as of version 1.1.
+ */
+
+ // REMIND: do we want all these??
+ // Others not here that we do want??
+
+ /* 2XX: generally "OK" */
+
+ /**
+ * HTTP Status-Code 200: OK.
+ */
+ public static final int HTTP_OK = 200;
+
+ /**
+ * HTTP Status-Code 201: Created.
+ */
+ public static final int HTTP_CREATED = 201;
+
+ /**
+ * HTTP Status-Code 202: Accepted.
+ */
+ public static final int HTTP_ACCEPTED = 202;
+
+ /**
+ * HTTP Status-Code 203: Non-Authoritative Information.
+ */
+ public static final int HTTP_NOT_AUTHORITATIVE = 203;
+
+ /**
+ * HTTP Status-Code 204: No Content.
+ */
+ public static final int HTTP_NO_CONTENT = 204;
+
+ /**
+ * HTTP Status-Code 205: Reset Content.
+ */
+ public static final int HTTP_RESET = 205;
+
+ /**
+ * HTTP Status-Code 206: Partial Content.
+ */
+ public static final int HTTP_PARTIAL = 206;
+
+ /* 3XX: relocation/redirect */
+
+ /**
+ * HTTP Status-Code 300: Multiple Choices.
+ */
+ public static final int HTTP_MULT_CHOICE = 300;
+
+ /**
+ * HTTP Status-Code 301: Moved Permanently.
+ */
+ public static final int HTTP_MOVED_PERM = 301;
+
+ /**
+ * HTTP Status-Code 302: Temporary Redirect.
+ */
+ public static final int HTTP_MOVED_TEMP = 302;
+
+ /**
+ * HTTP Status-Code 303: See Other.
+ */
+ public static final int HTTP_SEE_OTHER = 303;
+
+ /**
+ * HTTP Status-Code 304: Not Modified.
+ */
+ public static final int HTTP_NOT_MODIFIED = 304;
+
+ /**
+ * HTTP Status-Code 305: Use Proxy.
+ */
+ public static final int HTTP_USE_PROXY = 305;
+
+ /* 4XX: client error */
+
+ /**
+ * HTTP Status-Code 400: Bad Request.
+ */
+ public static final int HTTP_BAD_REQUEST = 400;
+
+ /**
+ * HTTP Status-Code 401: Unauthorized.
+ */
+ public static final int HTTP_UNAUTHORIZED = 401;
+
+ /**
+ * HTTP Status-Code 402: Payment Required.
+ */
+ public static final int HTTP_PAYMENT_REQUIRED = 402;
+
+ /**
+ * HTTP Status-Code 403: Forbidden.
+ */
+ public static final int HTTP_FORBIDDEN = 403;
+
+ /**
+ * HTTP Status-Code 404: Not Found.
+ */
+ public static final int HTTP_NOT_FOUND = 404;
+
+ /**
+ * HTTP Status-Code 405: Method Not Allowed.
+ */
+ public static final int HTTP_BAD_METHOD = 405;
+
+ /**
+ * HTTP Status-Code 406: Not Acceptable.
+ */
+ public static final int HTTP_NOT_ACCEPTABLE = 406;
+
+ /**
+ * HTTP Status-Code 407: Proxy Authentication Required.
+ */
+ public static final int HTTP_PROXY_AUTH = 407;
+
+ /**
+ * HTTP Status-Code 408: Request Time-Out.
+ */
+ public static final int HTTP_CLIENT_TIMEOUT = 408;
+
+ /**
+ * HTTP Status-Code 409: Conflict.
+ */
+ public static final int HTTP_CONFLICT = 409;
+
+ /**
+ * HTTP Status-Code 410: Gone.
+ */
+ public static final int HTTP_GONE = 410;
+
+ /**
+ * HTTP Status-Code 411: Length Required.
+ */
+ public static final int HTTP_LENGTH_REQUIRED = 411;
+
+ /**
+ * HTTP Status-Code 412: Precondition Failed.
+ */
+ public static final int HTTP_PRECON_FAILED = 412;
+
+ /**
+ * HTTP Status-Code 413: Request Entity Too Large.
+ */
+ public static final int HTTP_ENTITY_TOO_LARGE = 413;
+
+ /**
+ * HTTP Status-Code 414: Request-URI Too Large.
+ */
+ public static final int HTTP_REQ_TOO_LONG = 414;
+
+ /**
+ * HTTP Status-Code 415: Unsupported Media Type.
+ */
+ public static final int HTTP_UNSUPPORTED_TYPE = 415;
+
+ /* 5XX: server error */
+
+ /**
+ * HTTP Status-Code 500: Internal Server Error.
+ * @deprecated it is misplaced and shouldn't have existed.
+ */
+ @Deprecated
+ public static final int HTTP_SERVER_ERROR = 500;
+
+ /**
+ * HTTP Status-Code 500: Internal Server Error.
+ */
+ public static final int HTTP_INTERNAL_ERROR = 500;
+
+ /**
+ * HTTP Status-Code 501: Not Implemented.
+ */
+ public static final int HTTP_NOT_IMPLEMENTED = 501;
+
+ /**
+ * HTTP Status-Code 502: Bad Gateway.
+ */
+ public static final int HTTP_BAD_GATEWAY = 502;
+
+ /**
+ * HTTP Status-Code 503: Service Unavailable.
+ */
+ public static final int HTTP_UNAVAILABLE = 503;
+
+ /**
+ * HTTP Status-Code 504: Gateway Timeout.
+ */
+ public static final int HTTP_GATEWAY_TIMEOUT = 504;
+
+ /**
+ * HTTP Status-Code 505: HTTP Version Not Supported.
+ */
+ public static final int HTTP_VERSION = 505;
+
+}
diff --git a/java/net/IDN.java b/java/net/IDN.java
new file mode 100644
index 0000000..a18c3a8
--- /dev/null
+++ b/java/net/IDN.java
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.net;
+
+import android.icu.text.IDNA;
+
+/**
+ * Provides methods to convert internationalized domain names (IDNs) between
+ * a normal Unicode representation and an ASCII Compatible Encoding (ACE) representation.
+ * Internationalized domain names can use characters from the entire range of
+ * Unicode, while traditional domain names are restricted to ASCII characters.
+ * ACE is an encoding of Unicode strings that uses only ASCII characters and
+ * can be used with software (such as the Domain Name System) that only
+ * understands traditional domain names.
+ *
+ * <p>Internationalized domain names are defined in <a href="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</a>.
+ * RFC 3490 defines two operations: ToASCII and ToUnicode. These 2 operations employ
+ * <a href="http://www.ietf.org/rfc/rfc3491.txt">Nameprep</a> algorithm, which is a
+ * profile of <a href="http://www.ietf.org/rfc/rfc3454.txt">Stringprep</a>, and
+ * <a href="http://www.ietf.org/rfc/rfc3492.txt">Punycode</a> algorithm to convert
+ * domain name string back and forth.
+ *
+ * <p>The behavior of aforementioned conversion process can be adjusted by various flags:
+ * <ul>
+ * <li>If the ALLOW_UNASSIGNED flag is used, the domain name string to be converted
+ * can contain code points that are unassigned in Unicode 3.2, which is the
+ * Unicode version on which IDN conversion is based. If the flag is not used,
+ * the presence of such unassigned code points is treated as an error.
+ * <li>If the USE_STD3_ASCII_RULES flag is used, ASCII strings are checked against <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122</a> and <a href="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</a>.
+ * It is an error if they don't meet the requirements.
+ * </ul>
+ * These flags can be logically OR'ed together.
+ *
+ * <p>The security consideration is important with respect to internationalization
+ * domain name support. For example, English domain names may be <i>homographed</i>
+ * - maliciously misspelled by substitution of non-Latin letters.
+ * <a href="http://www.unicode.org/reports/tr36/">Unicode Technical Report #36</a>
+ * discusses security issues of IDN support as well as possible solutions.
+ * Applications are responsible for taking adequate security measures when using
+ * international domain names.
+ *
+ * @author Edward Wang
+ * @since 1.6
+ *
+ */
+public final class IDN {
+ /**
+ * Flag to allow processing of unassigned code points
+ */
+ public static final int ALLOW_UNASSIGNED = 0x01;
+
+ /**
+ * Flag to turn on the check against STD-3 ASCII rules
+ */
+ public static final int USE_STD3_ASCII_RULES = 0x02;
+
+
+ /**
+ * Translates a string from Unicode to ASCII Compatible Encoding (ACE),
+ * as defined by the ToASCII operation of <a href="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</a>.
+ *
+ * <p>ToASCII operation can fail. ToASCII fails if any step of it fails.
+ * If ToASCII operation fails, an IllegalArgumentException will be thrown.
+ * In this case, the input string should not be used in an internationalized domain name.
+ *
+ * <p> A label is an individual part of a domain name. The original ToASCII operation,
+ * as defined in RFC 3490, only operates on a single label. This method can handle
+ * both label and entire domain name, by assuming that labels in a domain name are
+ * always separated by dots. The following characters are recognized as dots:
+ * \u002E (full stop), \u3002 (ideographic full stop), \uFF0E (fullwidth full stop),
+ * and \uFF61 (halfwidth ideographic full stop). if dots are
+ * used as label separators, this method also changes all of them to \u002E (full stop)
+ * in output translated string.
+ *
+ * @param input the string to be processed
+ * @param flag process flag; can be 0 or any logical OR of possible flags
+ *
+ * @return the translated {@code String}
+ *
+ * @throws IllegalArgumentException if the input string doesn't conform to RFC 3490 specification
+ */
+ public static String toASCII(String input, int flag) {
+ // BEGIN Android-changed: Use ICU4J implementation
+ try {
+ return IDNA.convertIDNToASCII(input, flag).toString();
+ } catch (android.icu.text.StringPrepParseException e) {
+ // b/113787610: "." is a valid IDN but is rejected by ICU.
+ // Usage is relatively uncommon, so only check for it if ICU throws.
+ if (".".equals(input)) {
+ return input;
+ }
+ throw new IllegalArgumentException("Invalid input to toASCII: " + input, e);
+ }
+ // END Android-changed: Use ICU4J implementation
+ }
+
+
+ /**
+ * Translates a string from Unicode to ASCII Compatible Encoding (ACE),
+ * as defined by the ToASCII operation of <a href="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</a>.
+ *
+ * <p> This convenience method works as if by invoking the
+ * two-argument counterpart as follows:
+ * <blockquote>
+ * {@link #toASCII(String, int) toASCII}(input, 0);
+ * </blockquote>
+ *
+ * @param input the string to be processed
+ *
+ * @return the translated {@code String}
+ *
+ * @throws IllegalArgumentException if the input string doesn't conform to RFC 3490 specification
+ */
+ public static String toASCII(String input) {
+ return toASCII(input, 0);
+ }
+
+
+ /**
+ * Translates a string from ASCII Compatible Encoding (ACE) to Unicode,
+ * as defined by the ToUnicode operation of <a href="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</a>.
+ *
+ * <p>ToUnicode never fails. In case of any error, the input string is returned unmodified.
+ *
+ * <p> A label is an individual part of a domain name. The original ToUnicode operation,
+ * as defined in RFC 3490, only operates on a single label. This method can handle
+ * both label and entire domain name, by assuming that labels in a domain name are
+ * always separated by dots. The following characters are recognized as dots:
+ * \u002E (full stop), \u3002 (ideographic full stop), \uFF0E (fullwidth full stop),
+ * and \uFF61 (halfwidth ideographic full stop).
+ *
+ * @param input the string to be processed
+ * @param flag process flag; can be 0 or any logical OR of possible flags
+ *
+ * @return the translated {@code String}
+ */
+ public static String toUnicode(String input, int flag) {
+ // BEGIN Android-changed: Use ICU4J implementation
+ try {
+ // ICU only translates separators to ASCII for toASCII.
+ // Java expects the translation for toUnicode too.
+ return convertFullStop(IDNA.convertIDNToUnicode(input, flag)).toString();
+ } catch (android.icu.text.StringPrepParseException e) {
+ // The RI documentation explicitly states that if the conversion was unsuccessful
+ // the original string is returned.
+ return input;
+ }
+ // END Android-changed: Use ICU4J implementation
+ }
+
+ // BEGIN Android-added: Use ICU4J implementation
+ private static boolean isLabelSeperator(char c) {
+ return (c == '\u3002' || c == '\uff0e' || c == '\uff61');
+ }
+
+ private static StringBuffer convertFullStop(StringBuffer input) {
+ for (int i = 0; i < input.length(); i++) {
+ if (isLabelSeperator(input.charAt(i))) {
+ input.setCharAt(i, '.');
+ }
+ }
+ return input;
+ }
+ // END Android-added: Use ICU4J implementation
+
+ /**
+ * Translates a string from ASCII Compatible Encoding (ACE) to Unicode,
+ * as defined by the ToUnicode operation of <a href="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</a>.
+ *
+ * <p> This convenience method works as if by invoking the
+ * two-argument counterpart as follows:
+ * <blockquote>
+ * {@link #toUnicode(String, int) toUnicode}(input, 0);
+ * </blockquote>
+ *
+ * @param input the string to be processed
+ *
+ * @return the translated {@code String}
+ */
+ public static String toUnicode(String input) {
+ return toUnicode(input, 0);
+ }
+
+
+ /* ---------------- Private members -------------- */
+
+ // Android-removed: Private helper methods, unused because we use ICU.
+ /*
+ // ACE Prefix is "xn--"
+ private static final String ACE_PREFIX = "xn--";
+ private static final int ACE_PREFIX_LENGTH = ACE_PREFIX.length();
+
+ private static final int MAX_LABEL_LENGTH = 63;
+
+ // single instance of nameprep
+ private static StringPrep namePrep = null;
+
+ static {
+ InputStream stream = null;
+
+ try {
+ final String IDN_PROFILE = "uidna.spp";
+ if (System.getSecurityManager() != null) {
+ stream = AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
+ public InputStream run() {
+ return StringPrep.class.getResourceAsStream(IDN_PROFILE);
+ }
+ });
+ } else {
+ stream = StringPrep.class.getResourceAsStream(IDN_PROFILE);
+ }
+
+ namePrep = new StringPrep(stream);
+ stream.close();
+ } catch (IOException e) {
+ // should never reach here
+ assert false;
+ }
+ }
+ */
+
+ /* ---------------- Private operations -------------- */
+
+
+ //
+ // to suppress the default zero-argument constructor
+ //
+ private IDN() {}
+
+ // Android-removed: Private helper methods, unused because we use ICU.
+ /*
+ //
+ // toASCII operation; should only apply to a single label
+ //
+ private static String toASCIIInternal(String label, int flag)
+ {
+ // step 1
+ // Check if the string contains code points outside the ASCII range 0..0x7c.
+ boolean isASCII = isAllASCII(label);
+ StringBuffer dest;
+
+ // step 2
+ // perform the nameprep operation; flag ALLOW_UNASSIGNED is used here
+ if (!isASCII) {
+ UCharacterIterator iter = UCharacterIterator.getInstance(label);
+ try {
+ dest = namePrep.prepare(iter, flag);
+ } catch (java.text.ParseException e) {
+ throw new IllegalArgumentException(e);
+ }
+ } else {
+ dest = new StringBuffer(label);
+ }
+
+ // step 8, move forward to check the smallest number of the code points
+ // the length must be inside 1..63
+ if (dest.length() == 0) {
+ throw new IllegalArgumentException(
+ "Empty label is not a legal name");
+ }
+
+ // step 3
+ // Verify the absence of non-LDH ASCII code points
+ // 0..0x2c, 0x2e..0x2f, 0x3a..0x40, 0x5b..0x60, 0x7b..0x7f
+ // Verify the absence of leading and trailing hyphen
+ boolean useSTD3ASCIIRules = ((flag & USE_STD3_ASCII_RULES) != 0);
+ if (useSTD3ASCIIRules) {
+ for (int i = 0; i < dest.length(); i++) {
+ int c = dest.charAt(i);
+ if (isNonLDHAsciiCodePoint(c)) {
+ throw new IllegalArgumentException(
+ "Contains non-LDH ASCII characters");
+ }
+ }
+
+ if (dest.charAt(0) == '-' ||
+ dest.charAt(dest.length() - 1) == '-') {
+
+ throw new IllegalArgumentException(
+ "Has leading or trailing hyphen");
+ }
+ }
+
+ if (!isASCII) {
+ // step 4
+ // If all code points are inside 0..0x7f, skip to step 8
+ if (!isAllASCII(dest.toString())) {
+ // step 5
+ // verify the sequence does not begin with ACE prefix
+ if(!startsWithACEPrefix(dest)){
+
+ // step 6
+ // encode the sequence with punycode
+ try {
+ dest = Punycode.encode(dest, null);
+ } catch (java.text.ParseException e) {
+ throw new IllegalArgumentException(e);
+ }
+
+ dest = toASCIILower(dest);
+
+ // step 7
+ // prepend the ACE prefix
+ dest.insert(0, ACE_PREFIX);
+ } else {
+ throw new IllegalArgumentException("The input starts with the ACE Prefix");
+ }
+
+ }
+ }
+
+ // step 8
+ // the length must be inside 1..63
+ if (dest.length() > MAX_LABEL_LENGTH) {
+ throw new IllegalArgumentException("The label in the input is too long");
+ }
+
+ return dest.toString();
+ }
+
+ //
+ // toUnicode operation; should only apply to a single label
+ //
+ private static String toUnicodeInternal(String label, int flag) {
+ boolean[] caseFlags = null;
+ StringBuffer dest;
+
+ // step 1
+ // find out if all the codepoints in input are ASCII
+ boolean isASCII = isAllASCII(label);
+
+ if(!isASCII){
+ // step 2
+ // perform the nameprep operation; flag ALLOW_UNASSIGNED is used here
+ try {
+ UCharacterIterator iter = UCharacterIterator.getInstance(label);
+ dest = namePrep.prepare(iter, flag);
+ } catch (Exception e) {
+ // toUnicode never fails; if any step fails, return the input string
+ return label;
+ }
+ } else {
+ dest = new StringBuffer(label);
+ }
+
+ // step 3
+ // verify ACE Prefix
+ if(startsWithACEPrefix(dest)) {
+
+ // step 4
+ // Remove the ACE Prefix
+ String temp = dest.substring(ACE_PREFIX_LENGTH, dest.length());
+
+ try {
+ // step 5
+ // Decode using punycode
+ StringBuffer decodeOut = Punycode.decode(new StringBuffer(temp), null);
+
+ // step 6
+ // Apply toASCII
+ String toASCIIOut = toASCII(decodeOut.toString(), flag);
+
+ // step 7
+ // verify
+ if (toASCIIOut.equalsIgnoreCase(dest.toString())) {
+ // step 8
+ // return output of step 5
+ return decodeOut.toString();
+ }
+ } catch (Exception ignored) {
+ // no-op
+ }
+ }
+
+ // just return the input
+ return label;
+ }
+
+
+ //
+ // LDH stands for "letter/digit/hyphen", with characters restricted to the
+ // 26-letter Latin alphabet <A-Z a-z>, the digits <0-9>, and the hyphen
+ // <->.
+ // Non LDH refers to characters in the ASCII range, but which are not
+ // letters, digits or the hypen.
+ //
+ // non-LDH = 0..0x2C, 0x2E..0x2F, 0x3A..0x40, 0x5B..0x60, 0x7B..0x7F
+ //
+ private static boolean isNonLDHAsciiCodePoint(int ch){
+ return (0x0000 <= ch && ch <= 0x002C) ||
+ (0x002E <= ch && ch <= 0x002F) ||
+ (0x003A <= ch && ch <= 0x0040) ||
+ (0x005B <= ch && ch <= 0x0060) ||
+ (0x007B <= ch && ch <= 0x007F);
+ }
+
+ //
+ // search dots in a string and return the index of that character;
+ // or if there is no dots, return the length of input string
+ // dots might be: \u002E (full stop), \u3002 (ideographic full stop), \uFF0E (fullwidth full stop),
+ // and \uFF61 (halfwidth ideographic full stop).
+ //
+ private static int searchDots(String s, int start) {
+ int i;
+ for (i = start; i < s.length(); i++) {
+ if (isLabelSeparator(s.charAt(i))) {
+ break;
+ }
+ }
+
+ return i;
+ }
+
+ //
+ // to check if a string is a root label, ".".
+ //
+ private static boolean isRootLabel(String s) {
+ return (s.length() == 1 && isLabelSeparator(s.charAt(0)));
+ }
+
+ //
+ // to check if a character is a label separator, i.e. a dot character.
+ //
+ private static boolean isLabelSeparator(char c) {
+ return (c == '.' || c == '\u3002' || c == '\uFF0E' || c == '\uFF61');
+ }
+
+ //
+ // to check if a string only contains US-ASCII code point
+ //
+ private static boolean isAllASCII(String input) {
+ boolean isASCII = true;
+ for (int i = 0; i < input.length(); i++) {
+ int c = input.charAt(i);
+ if (c > 0x7F) {
+ isASCII = false;
+ break;
+ }
+ }
+ return isASCII;
+ }
+
+ //
+ // to check if a string starts with ACE-prefix
+ //
+ private static boolean startsWithACEPrefix(StringBuffer input){
+ boolean startsWithPrefix = true;
+
+ if(input.length() < ACE_PREFIX_LENGTH){
+ return false;
+ }
+ for(int i = 0; i < ACE_PREFIX_LENGTH; i++){
+ if(toASCIILower(input.charAt(i)) != ACE_PREFIX.charAt(i)){
+ startsWithPrefix = false;
+ }
+ }
+ return startsWithPrefix;
+ }
+
+ private static char toASCIILower(char ch){
+ if('A' <= ch && ch <= 'Z'){
+ return (char)(ch + 'a' - 'A');
+ }
+ return ch;
+ }
+
+ private static StringBuffer toASCIILower(StringBuffer input){
+ StringBuffer dest = new StringBuffer();
+ for(int i = 0; i < input.length();i++){
+ dest.append(toASCIILower(input.charAt(i)));
+ }
+ return dest;
+ }
+ */
+}
diff --git a/java/net/InMemoryCookieStore.java b/java/net/InMemoryCookieStore.java
new file mode 100644
index 0000000..5df66c0
--- /dev/null
+++ b/java/net/InMemoryCookieStore.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import dalvik.system.VMRuntime;
+
+import java.util.List;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.concurrent.locks.ReentrantLock;
+
+// Android-changed: App compat changes and bug fixes
+// b/26456024 Add targetSdkVersion based compatibility for domain matching
+// b/33034917 Support clearing cookies by adding it with "max-age=0"
+// b/25897688 InMemoryCookieStore ignores scheme (http/https) port and path of the cookie
+// Remove cookieJar and domainIndex. Use urlIndex as single Cookie storage
+// Fix InMemoryCookieStore#remove to verify cookie URI before removal
+// Fix InMemoryCookieStore#removeAll to return false if it's empty.
+/**
+ * A simple in-memory java.net.CookieStore implementation
+ *
+ * @author Edward Wang
+ * @since 1.6
+ * @hide Visible for testing only.
+ */
+public class InMemoryCookieStore implements CookieStore {
+ // the in-memory representation of cookies
+ // BEGIN Android-removed: Remove cookieJar and domainIndex
+ /*
+ private List<HttpCookie> cookieJar = null;
+
+ // the cookies are indexed by its domain and associated uri (if present)
+ // CAUTION: when a cookie removed from main data structure (i.e. cookieJar),
+ // it won't be cleared in domainIndex & uriIndex. Double-check the
+ // presence of cookie when retrieve one form index store.
+ private Map<String, List<HttpCookie>> domainIndex = null;
+ */
+ // END Android-removed: Remove cookieJar and domainIndex
+ private Map<URI, List<HttpCookie>> uriIndex = null;
+
+ // use ReentrantLock instead of syncronized for scalability
+ private ReentrantLock lock = null;
+
+ // BEGIN Android-changed: Add targetSdkVersion and remove cookieJar and domainIndex
+ private final boolean applyMCompatibility;
+
+ /**
+ * The default ctor
+ */
+ public InMemoryCookieStore() {
+ this(VMRuntime.getRuntime().getTargetSdkVersion());
+ }
+
+ public InMemoryCookieStore(int targetSdkVersion) {
+ uriIndex = new HashMap<>();
+ lock = new ReentrantLock(false);
+ applyMCompatibility = (targetSdkVersion <= 23);
+ }
+ // END Android-changed: Add targetSdkVersion and remove cookieJar and domainIndex
+
+ /**
+ * Add one cookie into cookie store.
+ */
+ public void add(URI uri, HttpCookie cookie) {
+ // pre-condition : argument can't be null
+ if (cookie == null) {
+ throw new NullPointerException("cookie is null");
+ }
+
+ lock.lock();
+ try {
+ // Android-changed: http://b/33034917, android supports clearing cookies
+ // by adding the cookie with max-age: 0.
+ //if (cookie.getMaxAge() != 0) {
+ addIndex(uriIndex, getEffectiveURI(uri), cookie);
+ //}
+ } finally {
+ lock.unlock();
+ }
+ }
+
+
+ /**
+ * Get all cookies, which:
+ * 1) given uri domain-matches with, or, associated with
+ * given uri when added to the cookie store.
+ * 3) not expired.
+ * See RFC 2965 sec. 3.3.4 for more detail.
+ */
+ public List<HttpCookie> get(URI uri) {
+ // argument can't be null
+ if (uri == null) {
+ throw new NullPointerException("uri is null");
+ }
+
+ List<HttpCookie> cookies = new ArrayList<HttpCookie>();
+ // BEGIN Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
+ lock.lock();
+ try {
+ // check domainIndex first
+ getInternal1(cookies, uriIndex, uri.getHost());
+ // check uriIndex then
+ getInternal2(cookies, uriIndex, getEffectiveURI(uri));
+ } finally {
+ lock.unlock();
+ }
+ // END Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
+ return cookies;
+ }
+
+ /**
+ * Get all cookies in cookie store, except those have expired
+ */
+ public List<HttpCookie> getCookies() {
+ // BEGIN Android-changed: Remove cookieJar and domainIndex
+ List<HttpCookie> rt = new ArrayList<HttpCookie>();
+
+ lock.lock();
+ try {
+ for (List<HttpCookie> list : uriIndex.values()) {
+ Iterator<HttpCookie> it = list.iterator();
+ while (it.hasNext()) {
+ HttpCookie cookie = it.next();
+ if (cookie.hasExpired()) {
+ it.remove();
+ } else if (!rt.contains(cookie)) {
+ rt.add(cookie);
+ }
+ }
+ }
+ } finally {
+ rt = Collections.unmodifiableList(rt);
+ lock.unlock();
+ }
+ // END Android-changed: Remove cookieJar and domainIndex
+
+ return rt;
+ }
+
+ /**
+ * Get all URIs, which are associated with at least one cookie
+ * of this cookie store.
+ */
+ public List<URI> getURIs() {
+ // BEGIN Android-changed: App compat. Return URI with no cookies. http://b/65538736
+ /*
+ List<URI> uris = new ArrayList<URI>();
+
+ lock.lock();
+ try {
+ Iterator<URI> it = uriIndex.keySet().iterator();
+ while (it.hasNext()) {
+ URI uri = it.next();
+ List<HttpCookie> cookies = uriIndex.get(uri);
+ if (cookies == null || cookies.size() == 0) {
+ // no cookies list or an empty list associated with
+ // this uri entry, delete it
+ it.remove();
+ }
+ }
+ } finally {
+ uris.addAll(uriIndex.keySet());
+ lock.unlock();
+ }
+
+ return uris;
+ */
+ lock.lock();
+ try {
+ List<URI> result = new ArrayList<URI>(uriIndex.keySet());
+ result.remove(null);
+ return Collections.unmodifiableList(result);
+ } finally {
+ lock.unlock();
+ }
+ // END Android-changed: App compat. Return URI with no cookies. http://b/65538736
+ }
+
+
+ /**
+ * Remove a cookie from store
+ */
+ public boolean remove(URI uri, HttpCookie ck) {
+ // argument can't be null
+ if (ck == null) {
+ throw new NullPointerException("cookie is null");
+ }
+
+ // BEGIN Android-changed: Fix uri not being removed from uriIndex
+ lock.lock();
+ try {
+ uri = getEffectiveURI(uri);
+ if (uriIndex.get(uri) == null) {
+ return false;
+ } else {
+ List<HttpCookie> cookies = uriIndex.get(uri);
+ if (cookies != null) {
+ return cookies.remove(ck);
+ } else {
+ return false;
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ // END Android-changed: Fix uri not being removed from uriIndex
+ }
+
+
+ /**
+ * Remove all cookies in this cookie store.
+ */
+ public boolean removeAll() {
+ lock.lock();
+ // BEGIN Android-changed: Let removeAll() return false when there are no cookies.
+ boolean result = false;
+
+ try {
+ result = !uriIndex.isEmpty();
+ uriIndex.clear();
+ } finally {
+ lock.unlock();
+ }
+
+ return result;
+ // END Android-changed: Let removeAll() return false when there are no cookies.
+ }
+
+
+ /* ---------------- Private operations -------------- */
+
+
+ /*
+ * This is almost the same as HttpCookie.domainMatches except for
+ * one difference: It won't reject cookies when the 'H' part of the
+ * domain contains a dot ('.').
+ * I.E.: RFC 2965 section 3.3.2 says that if host is x.y.domain.com
+ * and the cookie domain is .domain.com, then it should be rejected.
+ * However that's not how the real world works. Browsers don't reject and
+ * some sites, like yahoo.com do actually expect these cookies to be
+ * passed along.
+ * And should be used for 'old' style cookies (aka Netscape type of cookies)
+ */
+ private boolean netscapeDomainMatches(String domain, String host)
+ {
+ if (domain == null || host == null) {
+ return false;
+ }
+
+ // if there's no embedded dot in domain and domain is not .local
+ boolean isLocalDomain = ".local".equalsIgnoreCase(domain);
+ int embeddedDotInDomain = domain.indexOf('.');
+ if (embeddedDotInDomain == 0) {
+ embeddedDotInDomain = domain.indexOf('.', 1);
+ }
+ if (!isLocalDomain && (embeddedDotInDomain == -1 || embeddedDotInDomain == domain.length() - 1)) {
+ return false;
+ }
+
+ // if the host name contains no dot and the domain name is .local
+ int firstDotInHost = host.indexOf('.');
+ if (firstDotInHost == -1 && isLocalDomain) {
+ return true;
+ }
+
+ int domainLength = domain.length();
+ int lengthDiff = host.length() - domainLength;
+ if (lengthDiff == 0) {
+ // if the host name and the domain name are just string-compare euqal
+ return host.equalsIgnoreCase(domain);
+ } else if (lengthDiff > 0) {
+ // need to check H & D component
+ String D = host.substring(lengthDiff);
+
+ // Android-changed: b/26456024 targetSdkVersion based compatibility for domain matching
+ // Android M and earlier: Cookies with domain "foo.com" would not match "bar.foo.com".
+ // The RFC dictates that the user agent must treat those domains as if they had a
+ // leading period and must therefore match "bar.foo.com".
+ if (applyMCompatibility && !domain.startsWith(".")) {
+ return false;
+ }
+
+ return (D.equalsIgnoreCase(domain));
+ } else if (lengthDiff == -1) {
+ // if domain is actually .host
+ return (domain.charAt(0) == '.' &&
+ host.equalsIgnoreCase(domain.substring(1)));
+ }
+
+ return false;
+ }
+
+ private void getInternal1(List<HttpCookie> cookies, Map<URI, List<HttpCookie>> cookieIndex,
+ String host) {
+ // BEGIN Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
+ // Use a separate list to handle cookies that need to be removed so
+ // that there is no conflict with iterators.
+ ArrayList<HttpCookie> toRemove = new ArrayList<HttpCookie>();
+ for (Map.Entry<URI, List<HttpCookie>> entry : cookieIndex.entrySet()) {
+ List<HttpCookie> lst = entry.getValue();
+ for (HttpCookie c : lst) {
+ String domain = c.getDomain();
+ if ((c.getVersion() == 0 && netscapeDomainMatches(domain, host)) ||
+ (c.getVersion() == 1 && HttpCookie.domainMatches(domain, host))) {
+
+ // the cookie still in main cookie store
+ if (!c.hasExpired()) {
+ // don't add twice
+ if (!cookies.contains(c)) {
+ cookies.add(c);
+ }
+ } else {
+ toRemove.add(c);
+ }
+ }
+ }
+ // Clear up the cookies that need to be removed
+ for (HttpCookie c : toRemove) {
+ lst.remove(c);
+
+ }
+ toRemove.clear();
+ }
+ // END Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
+ }
+
+ // @param cookies [OUT] contains the found cookies
+ // @param cookieIndex the index
+ // @param comparator the prediction to decide whether or not
+ // a cookie in index should be returned
+ private <T extends Comparable<T>>
+ void getInternal2(List<HttpCookie> cookies, Map<T, List<HttpCookie>> cookieIndex,
+ T comparator)
+ {
+ // BEGIN Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
+ // Removed cookieJar
+ for (T index : cookieIndex.keySet()) {
+ if ((index == comparator) || (index != null && comparator.compareTo(index) == 0)) {
+ List<HttpCookie> indexedCookies = cookieIndex.get(index);
+ // check the list of cookies associated with this domain
+ if (indexedCookies != null) {
+ Iterator<HttpCookie> it = indexedCookies.iterator();
+ while (it.hasNext()) {
+ HttpCookie ck = it.next();
+ // the cookie still in main cookie store
+ if (!ck.hasExpired()) {
+ // don't add twice
+ if (!cookies.contains(ck))
+ cookies.add(ck);
+ } else {
+ it.remove();
+ }
+ }
+ } // end of indexedCookies != null
+ } // end of comparator.compareTo(index) == 0
+ } // end of cookieIndex iteration
+ // END Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
+ }
+
+ // add 'cookie' indexed by 'index' into 'indexStore'
+ private <T> void addIndex(Map<T, List<HttpCookie>> indexStore,
+ T index,
+ HttpCookie cookie)
+ {
+ // Android-changed: "index" can be null. We only use the URI based
+ // index on Android and we want to support null URIs. The underlying
+ // store is a HashMap which will support null keys anyway.
+ // if (index != null) {
+ List<HttpCookie> cookies = indexStore.get(index);
+ if (cookies != null) {
+ // there may already have the same cookie, so remove it first
+ cookies.remove(cookie);
+
+ cookies.add(cookie);
+ } else {
+ cookies = new ArrayList<HttpCookie>();
+ cookies.add(cookie);
+ indexStore.put(index, cookies);
+ }
+ }
+
+
+ //
+ // for cookie purpose, the effective uri should only be http://host
+ // the path will be taken into account when path-match algorithm applied
+ //
+ private URI getEffectiveURI(URI uri) {
+ URI effectiveURI = null;
+ // Android-added: Fix NullPointerException
+ if (uri == null) {
+ return null;
+ }
+ try {
+ effectiveURI = new URI("http",
+ uri.getHost(),
+ null, // path component
+ null, // query component
+ null // fragment component
+ );
+ } catch (URISyntaxException ignored) {
+ effectiveURI = uri;
+ }
+
+ return effectiveURI;
+ }
+}
diff --git a/java/net/Inet4Address.annotated.java b/java/net/Inet4Address.annotated.java
new file mode 100644
index 0000000..e10debf
--- /dev/null
+++ b/java/net/Inet4Address.annotated.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package java.net;
+
+import java.io.ObjectStreamException;
+
+@SuppressWarnings({"unchecked", "deprecation", "all"})
+public final class Inet4Address extends java.net.InetAddress {
+
+Inet4Address() { throw new RuntimeException("Stub!"); }
+
+public boolean isMulticastAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isAnyLocalAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isLoopbackAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isLinkLocalAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isSiteLocalAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCGlobal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCNodeLocal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCLinkLocal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCSiteLocal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCOrgLocal() { throw new RuntimeException("Stub!"); }
+
+public byte[] getAddress() { throw new RuntimeException("Stub!"); }
+
+public java.lang.String getHostAddress() { throw new RuntimeException("Stub!"); }
+
+public int hashCode() { throw new RuntimeException("Stub!"); }
+
+public boolean equals(java.lang.Object obj) { throw new RuntimeException("Stub!"); }
+
[email protected]
+public static final java.net.InetAddress ALL;
+static { ALL = null; }
+
[email protected]
+public static final java.net.InetAddress ANY;
+static { ANY = null; }
+
[email protected]
+public static final java.net.InetAddress LOOPBACK;
+static { LOOPBACK = null; }
+}
+
diff --git a/java/net/Inet4Address.java b/java/net/Inet4Address.java
new file mode 100644
index 0000000..1fb7b92
--- /dev/null
+++ b/java/net/Inet4Address.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.ObjectStreamException;
+import static android.system.OsConstants.*;
+
+/**
+ * This class represents an Internet Protocol version 4 (IPv4) address.
+ * Defined by <a href="http://www.ietf.org/rfc/rfc790.txt">
+ * <i>RFC 790: Assigned Numbers</i></a>,
+ * <a href="http://www.ietf.org/rfc/rfc1918.txt">
+ * <i>RFC 1918: Address Allocation for Private Internets</i></a>,
+ * and <a href="http://www.ietf.org/rfc/rfc2365.txt"><i>RFC 2365:
+ * Administratively Scoped IP Multicast</i></a>
+ *
+ * <h3> <A NAME="format">Textual representation of IP addresses</a> </h3>
+ *
+ * Textual representation of IPv4 address used as input to methods
+ * takes one of the following forms:
+ *
+ * <blockquote><table cellpadding=0 cellspacing=0 summary="layout">
+ * <tr><td>{@code d.d.d.d}</td></tr>
+ * <tr><td>{@code d.d.d}</td></tr>
+ * <tr><td>{@code d.d}</td></tr>
+ * <tr><td>{@code d}</td></tr>
+ * </table></blockquote>
+ *
+ * <p> When four parts are specified, each is interpreted as a byte of
+ * data and assigned, from left to right, to the four bytes of an IPv4
+ * address.
+
+ * <p> When a three part address is specified, the last part is
+ * interpreted as a 16-bit quantity and placed in the right most two
+ * bytes of the network address. This makes the three part address
+ * format convenient for specifying Class B net- work addresses as
+ * 128.net.host.
+ *
+ * <p> When a two part address is supplied, the last part is
+ * interpreted as a 24-bit quantity and placed in the right most three
+ * bytes of the network address. This makes the two part address
+ * format convenient for specifying Class A network addresses as
+ * net.host.
+ *
+ * <p> When only one part is given, the value is stored directly in
+ * the network address without any byte rearrangement.
+ *
+ * <p> For methods that return a textual representation as output
+ * value, the first form, i.e. a dotted-quad string, is used.
+ *
+ * <h4> The Scope of a Multicast Address </h4>
+ *
+ * Historically the IPv4 TTL field in the IP header has doubled as a
+ * multicast scope field: a TTL of 0 means node-local, 1 means
+ * link-local, up through 32 means site-local, up through 64 means
+ * region-local, up through 128 means continent-local, and up through
+ * 255 are global. However, the administrative scoping is preferred.
+ * Please refer to <a href="http://www.ietf.org/rfc/rfc2365.txt">
+ * <i>RFC 2365: Administratively Scoped IP Multicast</i></a>
+ * @since 1.4
+ */
+
+public final
+class Inet4Address extends InetAddress {
+ final static int INADDRSZ = 4;
+
+ /** use serialVersionUID from InetAddress, but Inet4Address instance
+ * is always replaced by an InetAddress instance before being
+ * serialized */
+ private static final long serialVersionUID = 3286316764910316507L;
+
+ // BEGIN Android-added: Define special-purpose IPv4 address
+ /** @hide */
+ public static final InetAddress ANY = new Inet4Address(null, new byte[] { 0, 0, 0, 0 });
+
+ /** @hide */
+ public static final InetAddress ALL =
+ new Inet4Address(null, new byte[] { (byte) 255, (byte) 255,
+ (byte) 255, (byte) 255 });
+ /** @hide */
+ public static final InetAddress LOOPBACK =
+ new Inet4Address("localhost", new byte[] { 127, 0, 0, 1 });
+ // END Android-added: Define special-purpose IPv4 address
+
+
+ // BEGIN Android-removed: Android doesn't need to call native init
+ /*
+ * Perform initializations.
+ *
+ static {
+ init();
+ }
+ */
+ // END Android-removed: Android doesn't need to call native init
+ Inet4Address() {
+ super();
+ holder().hostName = null;
+ holder().address = 0;
+ holder().family = AF_INET;
+ }
+
+ Inet4Address(String hostName, byte addr[]) {
+ holder().hostName = hostName;
+ holder().family = AF_INET;
+ if (addr != null) {
+ if (addr.length == INADDRSZ) {
+ int address = addr[3] & 0xFF;
+ address |= ((addr[2] << 8) & 0xFF00);
+ address |= ((addr[1] << 16) & 0xFF0000);
+ address |= ((addr[0] << 24) & 0xFF000000);
+ holder().address = address;
+ }
+ }
+ holder().originalHostName = hostName;
+ }
+ Inet4Address(String hostName, int address) {
+ holder().hostName = hostName;
+ holder().family = AF_INET;
+ holder().address = address;
+ holder().originalHostName = hostName;
+ }
+
+ /**
+ * Replaces the object to be serialized with an InetAddress object.
+ *
+ * @return the alternate object to be serialized.
+ *
+ * @throws ObjectStreamException if a new object replacing this
+ * object could not be created
+ */
+ private Object writeReplace() throws ObjectStreamException {
+ // will replace the to be serialized 'this' object
+ InetAddress inet = new InetAddress();
+ inet.holder().hostName = holder().getHostName();
+ inet.holder().address = holder().getAddress();
+
+ /**
+ * Prior to 1.4 an InetAddress was created with a family
+ * based on the platform AF_INET value (usually 2).
+ * For compatibility reasons we must therefore write the
+ * the InetAddress with this family.
+ */
+ inet.holder().family = 2;
+
+ return inet;
+ }
+
+ /**
+ * Utility routine to check if the InetAddress is an
+ * IP multicast address. IP multicast address is a Class D
+ * address i.e first four bits of the address are 1110.
+ * @return a {@code boolean} indicating if the InetAddress is
+ * an IP multicast address
+ * @since JDK1.1
+ */
+ public boolean isMulticastAddress() {
+ return ((holder().getAddress() & 0xf0000000) == 0xe0000000);
+ }
+
+ /**
+ * Utility routine to check if the InetAddress in a wildcard address.
+ * @return a {@code boolean} indicating if the Inetaddress is
+ * a wildcard address.
+ * @since 1.4
+ */
+ public boolean isAnyLocalAddress() {
+ return holder().getAddress() == 0;
+ }
+
+ /**
+ * Utility routine to check if the InetAddress is a loopback address.
+ *
+ * @return a {@code boolean} indicating if the InetAddress is
+ * a loopback address; or false otherwise.
+ * @since 1.4
+ */
+ public boolean isLoopbackAddress() {
+ /* 127.x.x.x */
+ byte[] byteAddr = getAddress();
+ return byteAddr[0] == 127;
+ }
+
+ /**
+ * Utility routine to check if the InetAddress is an link local address.
+ *
+ * @return a {@code boolean} indicating if the InetAddress is
+ * a link local address; or false if address is not a link local unicast address.
+ * @since 1.4
+ */
+ public boolean isLinkLocalAddress() {
+ // link-local unicast in IPv4 (169.254.0.0/16)
+ // defined in "Documenting Special Use IPv4 Address Blocks
+ // that have been Registered with IANA" by Bill Manning
+ // draft-manning-dsua-06.txt
+ int address = holder().getAddress();
+ return (((address >>> 24) & 0xFF) == 169)
+ && (((address >>> 16) & 0xFF) == 254);
+ }
+
+ /**
+ * Utility routine to check if the InetAddress is a site local address.
+ *
+ * @return a {@code boolean} indicating if the InetAddress is
+ * a site local address; or false if address is not a site local unicast address.
+ * @since 1.4
+ */
+ public boolean isSiteLocalAddress() {
+ // refer to RFC 1918
+ // 10/8 prefix
+ // 172.16/12 prefix
+ // 192.168/16 prefix
+ int address = holder().getAddress();
+ return (((address >>> 24) & 0xFF) == 10)
+ || ((((address >>> 24) & 0xFF) == 172)
+ && (((address >>> 16) & 0xF0) == 16))
+ || ((((address >>> 24) & 0xFF) == 192)
+ && (((address >>> 16) & 0xFF) == 168));
+ }
+
+ /**
+ * Utility routine to check if the multicast address has global scope.
+ *
+ * @return a {@code boolean} indicating if the address has
+ * is a multicast address of global scope, false if it is not
+ * of global scope or it is not a multicast address
+ * @since 1.4
+ */
+ public boolean isMCGlobal() {
+ // 224.0.1.0 to 238.255.255.255
+ byte[] byteAddr = getAddress();
+ return ((byteAddr[0] & 0xff) >= 224 && (byteAddr[0] & 0xff) <= 238 ) &&
+ !((byteAddr[0] & 0xff) == 224 && byteAddr[1] == 0 &&
+ byteAddr[2] == 0);
+ }
+
+ /**
+ * Utility routine to check if the multicast address has node scope.
+ *
+ * @return a {@code boolean} indicating if the address has
+ * is a multicast address of node-local scope, false if it is not
+ * of node-local scope or it is not a multicast address
+ * @since 1.4
+ */
+ public boolean isMCNodeLocal() {
+ // unless ttl == 0
+ return false;
+ }
+
+ /**
+ * Utility routine to check if the multicast address has link scope.
+ *
+ * @return a {@code boolean} indicating if the address has
+ * is a multicast address of link-local scope, false if it is not
+ * of link-local scope or it is not a multicast address
+ * @since 1.4
+ */
+ public boolean isMCLinkLocal() {
+ // 224.0.0/24 prefix and ttl == 1
+ int address = holder().getAddress();
+ return (((address >>> 24) & 0xFF) == 224)
+ && (((address >>> 16) & 0xFF) == 0)
+ && (((address >>> 8) & 0xFF) == 0);
+ }
+
+ /**
+ * Utility routine to check if the multicast address has site scope.
+ *
+ * @return a {@code boolean} indicating if the address has
+ * is a multicast address of site-local scope, false if it is not
+ * of site-local scope or it is not a multicast address
+ * @since 1.4
+ */
+ public boolean isMCSiteLocal() {
+ // 239.255/16 prefix or ttl < 32
+ int address = holder().getAddress();
+ return (((address >>> 24) & 0xFF) == 239)
+ && (((address >>> 16) & 0xFF) == 255);
+ }
+
+ /**
+ * Utility routine to check if the multicast address has organization scope.
+ *
+ * @return a {@code boolean} indicating if the address has
+ * is a multicast address of organization-local scope,
+ * false if it is not of organization-local scope
+ * or it is not a multicast address
+ * @since 1.4
+ */
+ public boolean isMCOrgLocal() {
+ // 239.192 - 239.195
+ int address = holder().getAddress();
+ return (((address >>> 24) & 0xFF) == 239)
+ && (((address >>> 16) & 0xFF) >= 192)
+ && (((address >>> 16) & 0xFF) <= 195);
+ }
+
+ /**
+ * Returns the raw IP address of this {@code InetAddress}
+ * object. The result is in network byte order: the highest order
+ * byte of the address is in {@code getAddress()[0]}.
+ *
+ * @return the raw IP address of this object.
+ */
+ public byte[] getAddress() {
+ int address = holder().getAddress();
+ byte[] addr = new byte[INADDRSZ];
+
+ addr[0] = (byte) ((address >>> 24) & 0xFF);
+ addr[1] = (byte) ((address >>> 16) & 0xFF);
+ addr[2] = (byte) ((address >>> 8) & 0xFF);
+ addr[3] = (byte) (address & 0xFF);
+ return addr;
+ }
+
+ /**
+ * Returns the IP address string in textual presentation form.
+ *
+ * @return the raw IP address in a string format.
+ * @since JDK1.0.2
+ */
+ public String getHostAddress() {
+ return numericToTextFormat(getAddress());
+ }
+
+ /**
+ * Returns a hashcode for this IP address.
+ *
+ * @return a hash code value for this IP address.
+ */
+ public int hashCode() {
+ return holder().getAddress();
+ }
+
+ /**
+ * Compares this object against the specified object.
+ * The result is {@code true} if and only if the argument is
+ * not {@code null} and it represents the same IP address as
+ * this object.
+ * <p>
+ * Two instances of {@code InetAddress} represent the same IP
+ * address if the length of the byte arrays returned by
+ * {@code getAddress} is the same for both, and each of the
+ * array components is the same for the byte arrays.
+ *
+ * @param obj the object to compare against.
+ * @return {@code true} if the objects are the same;
+ * {@code false} otherwise.
+ * @see java.net.InetAddress#getAddress()
+ */
+ public boolean equals(Object obj) {
+ return (obj != null) && (obj instanceof Inet4Address) &&
+ (((InetAddress)obj).holder().getAddress() == holder().getAddress());
+ }
+
+ // Utilities
+ /*
+ * Converts IPv4 binary address into a string suitable for presentation.
+ *
+ * @param src a byte array representing an IPv4 numeric address
+ * @return a String representing the IPv4 address in
+ * textual representation format
+ * @since 1.4
+ */
+
+ static String numericToTextFormat(byte[] src)
+ {
+ return (src[0] & 0xff) + "." + (src[1] & 0xff) + "." + (src[2] & 0xff) + "." + (src[3] & 0xff);
+ }
+
+ // BEGIN Android-removed: Android doesn't need to call native init
+ /*
+ * Perform class load-time initializations.
+ *
+ private static native void init();
+ */
+ // END Android-removed: Android doesn't need to call native init
+}
diff --git a/java/net/Inet6Address.annotated.java b/java/net/Inet6Address.annotated.java
new file mode 100644
index 0000000..7888710
--- /dev/null
+++ b/java/net/Inet6Address.annotated.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package java.net;
+
+
[email protected]
+@SuppressWarnings({"unchecked", "deprecation", "all"})
+public final class Inet6Address extends java.net.InetAddress {
+
+Inet6Address() { throw new RuntimeException("Stub!"); }
+
+public static java.net.Inet6Address getByAddress(java.lang.String host, byte[] addr, java.net.NetworkInterface nif) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+
+public static java.net.Inet6Address getByAddress(java.lang.String host, byte[] addr, int scope_id) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+
+public boolean isMulticastAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isAnyLocalAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isLoopbackAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isLinkLocalAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isSiteLocalAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCGlobal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCNodeLocal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCLinkLocal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCSiteLocal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCOrgLocal() { throw new RuntimeException("Stub!"); }
+
+public byte[] getAddress() { throw new RuntimeException("Stub!"); }
+
[email protected]
+public int getScopeId() { throw new RuntimeException("Stub!"); }
+
+public java.net.NetworkInterface getScopedInterface() { throw new RuntimeException("Stub!"); }
+
+public java.lang.String getHostAddress() { throw new RuntimeException("Stub!"); }
+
+public int hashCode() { throw new RuntimeException("Stub!"); }
+
+public boolean equals(java.lang.Object obj) { throw new RuntimeException("Stub!"); }
+
+public boolean isIPv4CompatibleAddress() { throw new RuntimeException("Stub!"); }
+
[email protected]
+public static final java.net.InetAddress ANY;
+static { ANY = null; }
+
[email protected]
+public static final java.net.InetAddress LOOPBACK;
+static { LOOPBACK = null; }
+}
diff --git a/java/net/Inet6Address.java b/java/net/Inet6Address.java
new file mode 100644
index 0000000..c0aadb3
--- /dev/null
+++ b/java/net/Inet6Address.java
@@ -0,0 +1,981 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.util.Enumeration;
+import java.util.Arrays;
+import libcore.io.Libcore;
+import static android.system.OsConstants.*;
+
+/**
+ * This class represents an Internet Protocol version 6 (IPv6) address.
+ * Defined by <a href="http://www.ietf.org/rfc/rfc2373.txt">
+ * <i>RFC 2373: IP Version 6 Addressing Architecture</i></a>.
+ *
+ * <h3> <A NAME="format">Textual representation of IP addresses</a> </h3>
+ *
+ * Textual representation of IPv6 address used as input to methods
+ * takes one of the following forms:
+ *
+ * <ol>
+ * <li><p> <A NAME="lform">The preferred form</a> is x:x:x:x:x:x:x:x,
+ * where the 'x's are
+ * the hexadecimal values of the eight 16-bit pieces of the
+ * address. This is the full form. For example,
+ *
+ * <blockquote><table cellpadding=0 cellspacing=0 summary="layout">
+ * <tr><td>{@code 1080:0:0:0:8:800:200C:417A}<td></tr>
+ * </table></blockquote>
+ *
+ * <p> Note that it is not necessary to write the leading zeros in
+ * an individual field. However, there must be at least one numeral
+ * in every field, except as described below.</li>
+ *
+ * <li><p> Due to some methods of allocating certain styles of IPv6
+ * addresses, it will be common for addresses to contain long
+ * strings of zero bits. In order to make writing addresses
+ * containing zero bits easier, a special syntax is available to
+ * compress the zeros. The use of "::" indicates multiple groups
+ * of 16-bits of zeros. The "::" can only appear once in an address.
+ * The "::" can also be used to compress the leading and/or trailing
+ * zeros in an address. For example,
+ *
+ * <blockquote><table cellpadding=0 cellspacing=0 summary="layout">
+ * <tr><td>{@code 1080::8:800:200C:417A}<td></tr>
+ * </table></blockquote>
+ *
+ * <li><p> An alternative form that is sometimes more convenient
+ * when dealing with a mixed environment of IPv4 and IPv6 nodes is
+ * x:x:x:x:x:x:d.d.d.d, where the 'x's are the hexadecimal values
+ * of the six high-order 16-bit pieces of the address, and the 'd's
+ * are the decimal values of the four low-order 8-bit pieces of the
+ * standard IPv4 representation address, for example,
+ *
+ * <blockquote><table cellpadding=0 cellspacing=0 summary="layout">
+ * <tr><td>{@code ::FFFF:129.144.52.38}<td></tr>
+ * <tr><td>{@code ::129.144.52.38}<td></tr>
+ * </table></blockquote>
+ *
+ * <p> where "::FFFF:d.d.d.d" and "::d.d.d.d" are, respectively, the
+ * general forms of an IPv4-mapped IPv6 address and an
+ * IPv4-compatible IPv6 address. Note that the IPv4 portion must be
+ * in the "d.d.d.d" form. The following forms are invalid:
+ *
+ * <blockquote><table cellpadding=0 cellspacing=0 summary="layout">
+ * <tr><td>{@code ::FFFF:d.d.d}<td></tr>
+ * <tr><td>{@code ::FFFF:d.d}<td></tr>
+ * <tr><td>{@code ::d.d.d}<td></tr>
+ * <tr><td>{@code ::d.d}<td></tr>
+ * </table></blockquote>
+ *
+ * <p> The following form:
+ *
+ * <blockquote><table cellpadding=0 cellspacing=0 summary="layout">
+ * <tr><td>{@code ::FFFF:d}<td></tr>
+ * </table></blockquote>
+ *
+ * <p> is valid, however it is an unconventional representation of
+ * the IPv4-compatible IPv6 address,
+ *
+ * <blockquote><table cellpadding=0 cellspacing=0 summary="layout">
+ * <tr><td>{@code ::255.255.0.d}<td></tr>
+ * </table></blockquote>
+ *
+ * <p> while "::d" corresponds to the general IPv6 address
+ * "0:0:0:0:0:0:0:d".</li>
+ * </ol>
+ *
+ * <p> For methods that return a textual representation as output
+ * value, the full form is used. Inet6Address will return the full
+ * form because it is unambiguous when used in combination with other
+ * textual data.
+ *
+ * <h4> Special IPv6 address </h4>
+ *
+ * <blockquote>
+ * <table cellspacing=2 summary="Description of IPv4-mapped address">
+ * <tr><th valign=top><i>IPv4-mapped address</i></th>
+ * <td>Of the form::ffff:w.x.y.z, this IPv6 address is used to
+ * represent an IPv4 address. It allows the native program to
+ * use the same address data structure and also the same
+ * socket when communicating with both IPv4 and IPv6 nodes.
+ *
+ * <p>In InetAddress and Inet6Address, it is used for internal
+ * representation; it has no functional role. Java will never
+ * return an IPv4-mapped address. These classes can take an
+ * IPv4-mapped address as input, both in byte array and text
+ * representation. However, it will be converted into an IPv4
+ * address.</td></tr>
+ * </table></blockquote>
+ *
+ * <h4><A NAME="scoped">Textual representation of IPv6 scoped addresses</a></h4>
+ *
+ * <p> The textual representation of IPv6 addresses as described above can be
+ * extended to specify IPv6 scoped addresses. This extension to the basic
+ * addressing architecture is described in [draft-ietf-ipngwg-scoping-arch-04.txt].
+ *
+ * <p> Because link-local and site-local addresses are non-global, it is possible
+ * that different hosts may have the same destination address and may be
+ * reachable through different interfaces on the same originating system. In
+ * this case, the originating system is said to be connected to multiple zones
+ * of the same scope. In order to disambiguate which is the intended destination
+ * zone, it is possible to append a zone identifier (or <i>scope_id</i>) to an
+ * IPv6 address.
+ *
+ * <p> The general format for specifying the <i>scope_id</i> is the following:
+ *
+ * <blockquote><i>IPv6-address</i>%<i>scope_id</i></blockquote>
+ * <p> The IPv6-address is a literal IPv6 address as described above.
+ * The <i>scope_id</i> refers to an interface on the local system, and it can be
+ * specified in two ways.
+ * <ol><li><i>As a numeric identifier.</i> This must be a positive integer
+ * that identifies the particular interface and scope as understood by the
+ * system. Usually, the numeric values can be determined through administration
+ * tools on the system. Each interface may have multiple values, one for each
+ * scope. If the scope is unspecified, then the default value used is zero.</li>
+ * <li><i>As a string.</i> This must be the exact string that is returned by
+ * {@link java.net.NetworkInterface#getName()} for the particular interface in
+ * question. When an Inet6Address is created in this way, the numeric scope-id
+ * is determined at the time the object is created by querying the relevant
+ * NetworkInterface.</li></ol>
+ *
+ * <p> Note also, that the numeric <i>scope_id</i> can be retrieved from
+ * Inet6Address instances returned from the NetworkInterface class. This can be
+ * used to find out the current scope ids configured on the system.
+ * @since 1.4
+ */
+
+public final
+class Inet6Address extends InetAddress {
+ final static int INADDRSZ = 16;
+
+ // BEGIN Android-removed: Remove special handling for link-local addresses
+ /*
+ * cached scope_id - for link-local address use only.
+ *
+ private transient int cached_scope_id; // 0
+ */
+ // END Android-removed: Remove special handling for link-local addresses
+
+ // BEGIN Android-added: Define special-purpose IPv6 address
+ /** @hide */
+ public static final InetAddress ANY =
+ new Inet6Address("::", new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0);
+
+ /** @hide */
+ public static final InetAddress LOOPBACK = new Inet6Address("ip6-localhost",
+ new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 0);
+ // END Android-added: Define special-purpose IPv6 address
+
+ private class Inet6AddressHolder {
+
+ private Inet6AddressHolder() {
+ ipaddress = new byte[INADDRSZ];
+ }
+
+ private Inet6AddressHolder(
+ byte[] ipaddress, int scope_id, boolean scope_id_set,
+ NetworkInterface ifname, boolean scope_ifname_set)
+ {
+ this.ipaddress = ipaddress;
+ this.scope_id = scope_id;
+ this.scope_id_set = scope_id_set;
+ this.scope_ifname_set = scope_ifname_set;
+ this.scope_ifname = ifname;
+ }
+
+ /**
+ * Holds a 128-bit (16 bytes) IPv6 address.
+ */
+ byte[] ipaddress;
+
+ /**
+ * scope_id. The scope specified when the object is created. If the object
+ * is created with an interface name, then the scope_id is not determined
+ * until the time it is needed.
+ */
+ int scope_id; // 0
+
+ /**
+ * This will be set to true when the scope_id field contains a valid
+ * integer scope_id.
+ */
+ boolean scope_id_set; // false
+
+ /**
+ * scoped interface. scope_id is derived from this as the scope_id of the first
+ * address whose scope is the same as this address for the named interface.
+ */
+ NetworkInterface scope_ifname; // null
+
+ /**
+ * set if the object is constructed with a scoped
+ * interface instead of a numeric scope id.
+ */
+ boolean scope_ifname_set; // false;
+
+ void setAddr(byte addr[]) {
+ if (addr.length == INADDRSZ) { // normal IPv6 address
+ System.arraycopy(addr, 0, ipaddress, 0, INADDRSZ);
+ }
+ }
+
+ void init(byte addr[], int scope_id) {
+ setAddr(addr);
+
+ // Android-changed: was >= 0
+ if (scope_id > 0) {
+ this.scope_id = scope_id;
+ this.scope_id_set = true;
+ }
+ }
+
+ void init(byte addr[], NetworkInterface nif)
+ throws UnknownHostException
+ {
+ setAddr(addr);
+
+ if (nif != null) {
+ this.scope_id = deriveNumericScope(ipaddress, nif);
+ this.scope_id_set = true;
+ this.scope_ifname = nif;
+ this.scope_ifname_set = true;
+ }
+ }
+
+ // Android-removed: getnameinfo returns smarter representations than getHostAddress()
+ /*
+ String getHostAddress() {
+ String s = numericToTextFormat(ipaddress);
+ if (scope_ifname != null) { // must check this first
+ s = s + "%" + scope_ifname.getName();
+ } else if (scope_id_set) {
+ s = s + "%" + scope_id;
+ }
+ return s;
+ }
+ */
+
+ public boolean equals(Object o) {
+ if (! (o instanceof Inet6AddressHolder)) {
+ return false;
+ }
+ Inet6AddressHolder that = (Inet6AddressHolder)o;
+
+ return Arrays.equals(this.ipaddress, that.ipaddress);
+ }
+
+ public int hashCode() {
+ if (ipaddress != null) {
+
+ int hash = 0;
+ int i=0;
+ while (i<INADDRSZ) {
+ int j=0;
+ int component=0;
+ while (j<4 && i<INADDRSZ) {
+ component = (component << 8) + ipaddress[i];
+ j++;
+ i++;
+ }
+ hash += component;
+ }
+ return hash;
+
+ } else {
+ return 0;
+ }
+ }
+
+ boolean isIPv4CompatibleAddress() {
+ if ((ipaddress[0] == 0x00) && (ipaddress[1] == 0x00) &&
+ (ipaddress[2] == 0x00) && (ipaddress[3] == 0x00) &&
+ (ipaddress[4] == 0x00) && (ipaddress[5] == 0x00) &&
+ (ipaddress[6] == 0x00) && (ipaddress[7] == 0x00) &&
+ (ipaddress[8] == 0x00) && (ipaddress[9] == 0x00) &&
+ (ipaddress[10] == 0x00) && (ipaddress[11] == 0x00)) {
+ return true;
+ }
+ return false;
+ }
+
+ boolean isMulticastAddress() {
+ return ((ipaddress[0] & 0xff) == 0xff);
+ }
+
+ boolean isAnyLocalAddress() {
+ byte test = 0x00;
+ for (int i = 0; i < INADDRSZ; i++) {
+ test |= ipaddress[i];
+ }
+ return (test == 0x00);
+ }
+
+ boolean isLoopbackAddress() {
+ byte test = 0x00;
+ for (int i = 0; i < 15; i++) {
+ test |= ipaddress[i];
+ }
+ return (test == 0x00) && (ipaddress[15] == 0x01);
+ }
+
+ boolean isLinkLocalAddress() {
+ return ((ipaddress[0] & 0xff) == 0xfe
+ && (ipaddress[1] & 0xc0) == 0x80);
+ }
+
+
+ boolean isSiteLocalAddress() {
+ return ((ipaddress[0] & 0xff) == 0xfe
+ && (ipaddress[1] & 0xc0) == 0xc0);
+ }
+
+ boolean isMCGlobal() {
+ return ((ipaddress[0] & 0xff) == 0xff
+ && (ipaddress[1] & 0x0f) == 0x0e);
+ }
+
+ boolean isMCNodeLocal() {
+ return ((ipaddress[0] & 0xff) == 0xff
+ && (ipaddress[1] & 0x0f) == 0x01);
+ }
+
+ boolean isMCLinkLocal() {
+ return ((ipaddress[0] & 0xff) == 0xff
+ && (ipaddress[1] & 0x0f) == 0x02);
+ }
+
+ boolean isMCSiteLocal() {
+ return ((ipaddress[0] & 0xff) == 0xff
+ && (ipaddress[1] & 0x0f) == 0x05);
+ }
+
+ boolean isMCOrgLocal() {
+ return ((ipaddress[0] & 0xff) == 0xff
+ && (ipaddress[1] & 0x0f) == 0x08);
+ }
+ }
+
+ private final transient Inet6AddressHolder holder6;
+
+ private static final long serialVersionUID = 6880410070516793377L;
+
+ // BEGIN Android-removed: Android doesn't need to call native init
+ /*
+ // Perform native initialization
+ static { init(); }
+ // END Android-removed: Android doesn't need to call native init
+ */
+
+ Inet6Address() {
+ super();
+ holder.init(null, AF_INET6);
+ holder6 = new Inet6AddressHolder();
+ }
+
+ /* checking of value for scope_id should be done by caller
+ * scope_id must be >= 0, or -1 to indicate not being set
+ */
+ Inet6Address(String hostName, byte addr[], int scope_id) {
+ holder.init(hostName, AF_INET6);
+ holder6 = new Inet6AddressHolder();
+ holder6.init(addr, scope_id);
+ }
+
+ Inet6Address(String hostName, byte addr[]) {
+ holder6 = new Inet6AddressHolder();
+ try {
+ initif (hostName, addr, null);
+ } catch (UnknownHostException e) {} /* cant happen if ifname is null */
+ }
+
+ Inet6Address (String hostName, byte addr[], NetworkInterface nif)
+ throws UnknownHostException
+ {
+ holder6 = new Inet6AddressHolder();
+ initif (hostName, addr, nif);
+ }
+
+ Inet6Address (String hostName, byte addr[], String ifname)
+ throws UnknownHostException
+ {
+ holder6 = new Inet6AddressHolder();
+ initstr (hostName, addr, ifname);
+ }
+
+ /**
+ * Create an Inet6Address in the exact manner of {@link
+ * InetAddress#getByAddress(String,byte[])} except that the IPv6 scope_id is
+ * set to the value corresponding to the given interface for the address
+ * type specified in {@code addr}. The call will fail with an
+ * UnknownHostException if the given interface does not have a numeric
+ * scope_id assigned for the given address type (eg. link-local or site-local).
+ * See <a href="Inet6Address.html#scoped">here</a> for a description of IPv6
+ * scoped addresses.
+ *
+ * @param host the specified host
+ * @param addr the raw IP address in network byte order
+ * @param nif an interface this address must be associated with.
+ * @return an Inet6Address object created from the raw IP address.
+ * @throws UnknownHostException
+ * if IP address is of illegal length, or if the interface does not
+ * have a numeric scope_id assigned for the given address type.
+ *
+ * @since 1.5
+ */
+ public static Inet6Address getByAddress(String host, byte[] addr,
+ NetworkInterface nif)
+ throws UnknownHostException
+ {
+ if (host != null && host.length() > 0 && host.charAt(0) == '[') {
+ if (host.charAt(host.length()-1) == ']') {
+ host = host.substring(1, host.length() -1);
+ }
+ }
+ if (addr != null) {
+ if (addr.length == Inet6Address.INADDRSZ) {
+ return new Inet6Address(host, addr, nif);
+ }
+ }
+ throw new UnknownHostException("addr is of illegal length");
+ }
+
+ /**
+ * Create an Inet6Address in the exact manner of {@link
+ * InetAddress#getByAddress(String,byte[])} except that the IPv6 scope_id is
+ * set to the given numeric value. The scope_id is not checked to determine
+ * if it corresponds to any interface on the system.
+ * See <a href="Inet6Address.html#scoped">here</a> for a description of IPv6
+ * scoped addresses.
+ *
+ * @param host the specified host
+ * @param addr the raw IP address in network byte order
+ * @param scope_id the numeric scope_id for the address.
+ * @return an Inet6Address object created from the raw IP address.
+ * @throws UnknownHostException if IP address is of illegal length.
+ *
+ * @since 1.5
+ */
+ public static Inet6Address getByAddress(String host, byte[] addr,
+ int scope_id)
+ throws UnknownHostException
+ {
+ if (host != null && host.length() > 0 && host.charAt(0) == '[') {
+ if (host.charAt(host.length()-1) == ']') {
+ host = host.substring(1, host.length() -1);
+ }
+ }
+ if (addr != null) {
+ if (addr.length == Inet6Address.INADDRSZ) {
+ return new Inet6Address(host, addr, scope_id);
+ }
+ }
+ throw new UnknownHostException("addr is of illegal length");
+ }
+
+ private void initstr(String hostName, byte addr[], String ifname)
+ throws UnknownHostException
+ {
+ try {
+ NetworkInterface nif = NetworkInterface.getByName (ifname);
+ if (nif == null) {
+ throw new UnknownHostException ("no such interface " + ifname);
+ }
+ initif (hostName, addr, nif);
+ } catch (SocketException e) {
+ throw new UnknownHostException ("SocketException thrown" + ifname);
+ }
+ }
+
+ private void initif(String hostName, byte addr[], NetworkInterface nif)
+ throws UnknownHostException
+ {
+ int family = -1;
+ holder6.init(addr, nif);
+
+ if (addr.length == INADDRSZ) { // normal IPv6 address
+ family = AF_INET6;
+ }
+ holder.init(hostName, family);
+ }
+
+ /* check the two Ipv6 addresses and return false if they are both
+ * non global address types, but not the same.
+ * (ie. one is sitelocal and the other linklocal)
+ * return true otherwise.
+ */
+
+ private static boolean isDifferentLocalAddressType(
+ byte[] thisAddr, byte[] otherAddr) {
+
+ if (Inet6Address.isLinkLocalAddress(thisAddr) &&
+ !Inet6Address.isLinkLocalAddress(otherAddr)) {
+ return false;
+ }
+ if (Inet6Address.isSiteLocalAddress(thisAddr) &&
+ !Inet6Address.isSiteLocalAddress(otherAddr)) {
+ return false;
+ }
+ return true;
+ }
+
+ private static int deriveNumericScope (byte[] thisAddr, NetworkInterface ifc) throws UnknownHostException {
+ Enumeration<InetAddress> addresses = ifc.getInetAddresses();
+ while (addresses.hasMoreElements()) {
+ InetAddress addr = addresses.nextElement();
+ if (!(addr instanceof Inet6Address)) {
+ continue;
+ }
+ Inet6Address ia6_addr = (Inet6Address)addr;
+ /* check if site or link local prefixes match */
+ if (!isDifferentLocalAddressType(thisAddr, ia6_addr.getAddress())){
+ /* type not the same, so carry on searching */
+ continue;
+ }
+ /* found a matching address - return its scope_id */
+ return ia6_addr.getScopeId();
+ }
+ throw new UnknownHostException ("no scope_id found");
+ }
+
+ private int deriveNumericScope (String ifname) throws UnknownHostException {
+ Enumeration<NetworkInterface> en;
+ try {
+ en = NetworkInterface.getNetworkInterfaces();
+ } catch (SocketException e) {
+ throw new UnknownHostException ("could not enumerate local network interfaces");
+ }
+ while (en.hasMoreElements()) {
+ NetworkInterface ifc = en.nextElement();
+ if (ifc.getName().equals (ifname)) {
+ return deriveNumericScope(holder6.ipaddress, ifc);
+ }
+ }
+ throw new UnknownHostException ("No matching address found for interface : " +ifname);
+ }
+
+ /**
+ * @serialField ipaddress byte[]
+ * @serialField scope_id int
+ * @serialField scope_id_set boolean
+ * @serialField scope_ifname_set boolean
+ * @serialField ifname String
+ */
+
+ private static final ObjectStreamField[] serialPersistentFields = {
+ new ObjectStreamField("ipaddress", byte[].class),
+ new ObjectStreamField("scope_id", int.class),
+ new ObjectStreamField("scope_id_set", boolean.class),
+ new ObjectStreamField("scope_ifname_set", boolean.class),
+ new ObjectStreamField("ifname", String.class)
+ };
+
+ private static final long FIELDS_OFFSET;
+ private static final sun.misc.Unsafe UNSAFE;
+
+ static {
+ try {
+ sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
+ FIELDS_OFFSET = unsafe.objectFieldOffset(
+ Inet6Address.class.getDeclaredField("holder6"));
+ UNSAFE = unsafe;
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
+
+ /**
+ * restore the state of this object from stream
+ * including the scope information, only if the
+ * scoped interface name is valid on this system
+ */
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException {
+ NetworkInterface scope_ifname = null;
+
+ // Android-changed: was getClass().getClassLoader() != null
+ if (getClass().getClassLoader() != Class.class.getClassLoader()) {
+ throw new SecurityException ("invalid address type");
+ }
+
+ ObjectInputStream.GetField gf = s.readFields();
+ byte[] ipaddress = (byte[])gf.get("ipaddress", null);
+ int scope_id = (int)gf.get("scope_id", -1);
+ boolean scope_id_set = (boolean)gf.get("scope_id_set", false);
+ boolean scope_ifname_set = (boolean)gf.get("scope_ifname_set", false);
+ String ifname = (String)gf.get("ifname", null);
+
+ if (ifname != null && !"".equals (ifname)) {
+ try {
+ scope_ifname = NetworkInterface.getByName(ifname);
+ if (scope_ifname == null) {
+ /* the interface does not exist on this system, so we clear
+ * the scope information completely */
+ scope_id_set = false;
+ scope_ifname_set = false;
+ scope_id = 0;
+ } else {
+ scope_ifname_set = true;
+ try {
+ scope_id = deriveNumericScope (ipaddress, scope_ifname);
+ } catch (UnknownHostException e) {
+ // typically should not happen, but it may be that
+ // the machine being used for deserialization has
+ // the same interface name but without IPv6 configured.
+ }
+ }
+ } catch (SocketException e) {}
+ }
+
+ /* if ifname was not supplied, then the numeric info is used */
+
+ ipaddress = ipaddress.clone();
+
+ // Check that our invariants are satisfied
+ if (ipaddress.length != INADDRSZ) {
+ throw new InvalidObjectException("invalid address length: "+
+ ipaddress.length);
+ }
+
+ if (holder().getFamily() != AF_INET6) {
+ throw new InvalidObjectException("invalid address family type");
+ }
+
+ Inet6AddressHolder h = new Inet6AddressHolder(
+ ipaddress, scope_id, scope_id_set, scope_ifname, scope_ifname_set
+ );
+
+ UNSAFE.putObject(this, FIELDS_OFFSET, h);
+ }
+
+ /**
+ * default behavior is overridden in order to write the
+ * scope_ifname field as a String, rather than a NetworkInterface
+ * which is not serializable
+ */
+ private synchronized void writeObject(ObjectOutputStream s)
+ throws IOException
+ {
+ String ifname = null;
+
+ if (holder6.scope_ifname != null) {
+ ifname = holder6.scope_ifname.getName();
+ holder6.scope_ifname_set = true;
+ }
+ ObjectOutputStream.PutField pfields = s.putFields();
+ pfields.put("ipaddress", holder6.ipaddress);
+ pfields.put("scope_id", holder6.scope_id);
+ pfields.put("scope_id_set", holder6.scope_id_set);
+ pfields.put("scope_ifname_set", holder6.scope_ifname_set);
+ pfields.put("ifname", ifname);
+ s.writeFields();
+ }
+
+ /**
+ * Utility routine to check if the InetAddress is an IP multicast
+ * address. 11111111 at the start of the address identifies the
+ * address as being a multicast address.
+ *
+ * @return a {@code boolean} indicating if the InetAddress is an IP
+ * multicast address
+ *
+ * @since JDK1.1
+ */
+ @Override
+ public boolean isMulticastAddress() {
+ return holder6.isMulticastAddress();
+ }
+
+ /**
+ * Utility routine to check if the InetAddress in a wildcard address.
+ *
+ * @return a {@code boolean} indicating if the Inetaddress is
+ * a wildcard address.
+ *
+ * @since 1.4
+ */
+ @Override
+ public boolean isAnyLocalAddress() {
+ return holder6.isAnyLocalAddress();
+ }
+
+ /**
+ * Utility routine to check if the InetAddress is a loopback address.
+ *
+ * @return a {@code boolean} indicating if the InetAddress is a loopback
+ * address; or false otherwise.
+ *
+ * @since 1.4
+ */
+ @Override
+ public boolean isLoopbackAddress() {
+ return holder6.isLoopbackAddress();
+ }
+
+ /**
+ * Utility routine to check if the InetAddress is an link local address.
+ *
+ * @return a {@code boolean} indicating if the InetAddress is a link local
+ * address; or false if address is not a link local unicast address.
+ *
+ * @since 1.4
+ */
+ @Override
+ public boolean isLinkLocalAddress() {
+ return holder6.isLinkLocalAddress();
+ }
+
+ /* static version of above */
+ static boolean isLinkLocalAddress(byte[] ipaddress) {
+ return ((ipaddress[0] & 0xff) == 0xfe
+ && (ipaddress[1] & 0xc0) == 0x80);
+ }
+
+ /**
+ * Utility routine to check if the InetAddress is a site local address.
+ *
+ * @return a {@code boolean} indicating if the InetAddress is a site local
+ * address; or false if address is not a site local unicast address.
+ *
+ * @since 1.4
+ */
+ @Override
+ public boolean isSiteLocalAddress() {
+ return holder6.isSiteLocalAddress();
+ }
+
+ /* static version of above */
+ static boolean isSiteLocalAddress(byte[] ipaddress) {
+ return ((ipaddress[0] & 0xff) == 0xfe
+ && (ipaddress[1] & 0xc0) == 0xc0);
+ }
+
+ /**
+ * Utility routine to check if the multicast address has global scope.
+ *
+ * @return a {@code boolean} indicating if the address has is a multicast
+ * address of global scope, false if it is not of global scope or
+ * it is not a multicast address
+ *
+ * @since 1.4
+ */
+ @Override
+ public boolean isMCGlobal() {
+ return holder6.isMCGlobal();
+ }
+
+ /**
+ * Utility routine to check if the multicast address has node scope.
+ *
+ * @return a {@code boolean} indicating if the address has is a multicast
+ * address of node-local scope, false if it is not of node-local
+ * scope or it is not a multicast address
+ *
+ * @since 1.4
+ */
+ @Override
+ public boolean isMCNodeLocal() {
+ return holder6.isMCNodeLocal();
+ }
+
+ /**
+ * Utility routine to check if the multicast address has link scope.
+ *
+ * @return a {@code boolean} indicating if the address has is a multicast
+ * address of link-local scope, false if it is not of link-local
+ * scope or it is not a multicast address
+ *
+ * @since 1.4
+ */
+ @Override
+ public boolean isMCLinkLocal() {
+ return holder6.isMCLinkLocal();
+ }
+
+ /**
+ * Utility routine to check if the multicast address has site scope.
+ *
+ * @return a {@code boolean} indicating if the address has is a multicast
+ * address of site-local scope, false if it is not of site-local
+ * scope or it is not a multicast address
+ *
+ * @since 1.4
+ */
+ @Override
+ public boolean isMCSiteLocal() {
+ return holder6.isMCSiteLocal();
+ }
+
+ /**
+ * Utility routine to check if the multicast address has organization scope.
+ *
+ * @return a {@code boolean} indicating if the address has is a multicast
+ * address of organization-local scope, false if it is not of
+ * organization-local scope or it is not a multicast address
+ *
+ * @since 1.4
+ */
+ @Override
+ public boolean isMCOrgLocal() {
+ return holder6.isMCOrgLocal();
+ }
+ /**
+ * Returns the raw IP address of this {@code InetAddress} object. The result
+ * is in network byte order: the highest order byte of the address is in
+ * {@code getAddress()[0]}.
+ *
+ * @return the raw IP address of this object.
+ */
+ @Override
+ public byte[] getAddress() {
+ return holder6.ipaddress.clone();
+ }
+
+ /**
+ * Returns the numeric scopeId, if this instance is associated with
+ * an interface. If no scoped_id is set, the returned value is zero.
+ *
+ * @return the scopeId, or zero if not set.
+ *
+ * @since 1.5
+ */
+ public int getScopeId() {
+ return holder6.scope_id;
+ }
+
+ /**
+ * Returns the scoped interface, if this instance was created with
+ * with a scoped interface.
+ *
+ * @return the scoped interface, or null if not set.
+ * @since 1.5
+ */
+ public NetworkInterface getScopedInterface() {
+ return holder6.scope_ifname;
+ }
+
+ /**
+ * Returns the IP address string in textual presentation. If the instance
+ * was created specifying a scope identifier then the scope id is appended
+ * to the IP address preceded by a "%" (per-cent) character. This can be
+ * either a numeric value or a string, depending on which was used to create
+ * the instance.
+ *
+ * @return the raw IP address in a string format.
+ */
+ @Override
+ public String getHostAddress() {
+ // Android-changed: getnameinfo returns smarter representations than getHostAddress()
+ // return holder6.getHostAddress();
+ return Libcore.os.getnameinfo(this, NI_NUMERICHOST); // Can't throw.
+ }
+
+ /**
+ * Returns a hashcode for this IP address.
+ *
+ * @return a hash code value for this IP address.
+ */
+ @Override
+ public int hashCode() {
+ return holder6.hashCode();
+ }
+
+ /**
+ * Compares this object against the specified object. The result is {@code
+ * true} if and only if the argument is not {@code null} and it represents
+ * the same IP address as this object.
+ *
+ * <p> Two instances of {@code InetAddress} represent the same IP address
+ * if the length of the byte arrays returned by {@code getAddress} is the
+ * same for both, and each of the array components is the same for the byte
+ * arrays.
+ *
+ * @param obj the object to compare against.
+ *
+ * @return {@code true} if the objects are the same; {@code false} otherwise.
+ *
+ * @see java.net.InetAddress#getAddress()
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof Inet6Address))
+ return false;
+
+ Inet6Address inetAddr = (Inet6Address)obj;
+
+ return holder6.equals(inetAddr.holder6);
+ }
+
+ /**
+ * Utility routine to check if the InetAddress is an
+ * IPv4 compatible IPv6 address.
+ *
+ * @return a {@code boolean} indicating if the InetAddress is an IPv4
+ * compatible IPv6 address; or false if address is IPv4 address.
+ *
+ * @since 1.4
+ */
+ public boolean isIPv4CompatibleAddress() {
+ return holder6.isIPv4CompatibleAddress();
+ }
+
+ // Utilities
+ private final static int INT16SZ = 2;
+
+ /*
+ * Convert IPv6 binary address into presentation (printable) format.
+ *
+ * @param src a byte array representing the IPv6 numeric address
+ * @return a String representing an IPv6 address in
+ * textual representation format
+ * @since 1.4
+ */
+ static String numericToTextFormat(byte[] src) {
+ StringBuilder sb = new StringBuilder(39);
+ for (int i = 0; i < (INADDRSZ / INT16SZ); i++) {
+ sb.append(Integer.toHexString(((src[i<<1]<<8) & 0xff00)
+ | (src[(i<<1)+1] & 0xff)));
+ if (i < (INADDRSZ / INT16SZ) -1 ) {
+ sb.append(":");
+ }
+ }
+ return sb.toString();
+ }
+
+ // BEGIN Android-removed: Android doesn't need to call native init
+ /*
+ * Perform class load-time initializations.
+ *
+ private static native void init();
+ */
+ // END Android-removed: Android doesn't need to call native init
+}
diff --git a/java/net/Inet6AddressImpl.java b/java/net/Inet6AddressImpl.java
new file mode 100644
index 0000000..84bcf17
--- /dev/null
+++ b/java/net/Inet6AddressImpl.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.net;
+import android.system.ErrnoException;
+import android.system.GaiException;
+import android.system.StructAddrinfo;
+import android.system.IcmpHeaders;
+
+import dalvik.system.BlockGuard;
+
+import libcore.io.IoBridge;
+import libcore.io.Libcore;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import libcore.net.InetAddressUtils;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.AF_UNSPEC;
+import static android.system.OsConstants.AI_ADDRCONFIG;
+import static android.system.OsConstants.EACCES;
+import static android.system.OsConstants.ECONNREFUSED;
+import static android.system.OsConstants.EPERM;
+import static android.system.OsConstants.NI_NAMEREQD;
+import static android.system.OsConstants.ICMP6_ECHO_REPLY;
+import static android.system.OsConstants.ICMP_ECHOREPLY;
+import static android.system.OsConstants.IPPROTO_ICMP;
+import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_STREAM;
+
+// Android-note: Android-specific behavior and Linux-based implementation
+// http://b/36933260 Implement root-less ICMP for isReachable()
+// http://b/28609551 Rewrite getHostByAddr0 using POSIX library Libcore.os.
+// http://b/25861497 Add BlockGuard checks.
+// http://b/26700324 Fix odd dependency chains of the static InetAddress.
+// anyLocalAddress() Let anyLocalAddress() always return an IPv6 address.
+// Let loopbackAddresses() return both Inet4 and Inet6 loopbacks.
+// Rewrote hostname lookup methods on top of Libcore.os. Merge implementation from InetAddress
+// and remove native methods in this class
+/*
+ * Package private implementation of InetAddressImpl for dual
+ * IPv4/IPv6 stack. {@code #anyLocalAddress()} will always return an IPv6 address.
+ *
+ * @since 1.4
+ */
+
+class Inet6AddressImpl implements InetAddressImpl {
+
+ // @GuardedBy(Inet6AddressImpl.class)
+ private static InetAddress anyLocalAddress;
+ // @GuardedBy(Inet6AddressImpl.class)
+ private static InetAddress[] loopbackAddresses;
+
+ private static final AddressCache addressCache = new AddressCache();
+
+ // BEGIN Android-changed: Rewrote hostname lookup methods on top of Libcore.os.
+ /*
+ public native String getLocalHostName() throws UnknownHostException;
+ public native InetAddress[]
+ lookupAllHostAddr(String hostname) throws UnknownHostException;
+ public native String getHostByAddr(byte[] addr) throws UnknownHostException;
+ private native boolean isReachable0(byte[] addr, int scope, int timeout, byte[] inf, int ttl, int if_scope) throws IOException;
+ */
+ @Override
+ public InetAddress[] lookupAllHostAddr(String host, int netId) throws UnknownHostException {
+ if (host == null || host.isEmpty()) {
+ // Android-changed: Return both the Inet4 and Inet6 loopback addresses
+ // when host == null or empty.
+ return loopbackAddresses();
+ }
+
+ // Is it a numeric address?
+ InetAddress result = InetAddressUtils.parseNumericAddressNoThrowStripOptionalBrackets(host);
+ if (result != null) {
+ return new InetAddress[] { result };
+ }
+
+ return lookupHostByName(host, netId);
+ }
+
+ /**
+ * Resolves a hostname to its IP addresses using a cache.
+ *
+ * @param host the hostname to resolve.
+ * @param netId the network to perform resolution upon.
+ * @return the IP addresses of the host.
+ */
+ private static InetAddress[] lookupHostByName(String host, int netId)
+ throws UnknownHostException {
+ BlockGuard.getThreadPolicy().onNetwork();
+ // Do we have a result cached?
+ Object cachedResult = addressCache.get(host, netId);
+ if (cachedResult != null) {
+ if (cachedResult instanceof InetAddress[]) {
+ // A cached positive result.
+ return (InetAddress[]) cachedResult;
+ } else {
+ // A cached negative result.
+ throw new UnknownHostException((String) cachedResult);
+ }
+ }
+ try {
+ StructAddrinfo hints = new StructAddrinfo();
+ hints.ai_flags = AI_ADDRCONFIG;
+ hints.ai_family = AF_UNSPEC;
+ // If we don't specify a socket type, every address will appear twice, once
+ // for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family
+ // anyway, just pick one.
+ hints.ai_socktype = SOCK_STREAM;
+ InetAddress[] addresses = Libcore.os.android_getaddrinfo(host, hints, netId);
+ // TODO: should getaddrinfo set the hostname of the InetAddresses it returns?
+ for (InetAddress address : addresses) {
+ address.holder().hostName = host;
+ address.holder().originalHostName = host;
+ }
+ addressCache.put(host, netId, addresses);
+ return addresses;
+ } catch (GaiException gaiException) {
+ // If the failure appears to have been a lack of INTERNET permission, throw a clear
+ // SecurityException to aid in debugging this common mistake.
+ // http://code.google.com/p/android/issues/detail?id=15722
+ if (gaiException.getCause() instanceof ErrnoException) {
+ int errno = ((ErrnoException) gaiException.getCause()).errno;
+ if (errno == EACCES || errno == EPERM) {
+ throw new SecurityException("Permission denied (missing INTERNET permission?)", gaiException);
+ }
+ }
+ // Otherwise, throw an UnknownHostException.
+ String detailMessage = "Unable to resolve host \"" + host + "\": " + Libcore.os.gai_strerror(gaiException.error);
+ addressCache.putUnknownHost(host, netId, detailMessage);
+ throw gaiException.rethrowAsUnknownHostException(detailMessage);
+ }
+ }
+
+ @Override
+ public String getHostByAddr(byte[] addr) throws UnknownHostException {
+ BlockGuard.getThreadPolicy().onNetwork();
+
+ return getHostByAddr0(addr);
+ }
+
+ @Override
+ public void clearAddressCache() {
+ addressCache.clear();
+ }
+ // END Android-changed: Rewrote hostname lookup methods on top of Libcore.os.
+
+ @Override
+ public boolean isReachable(InetAddress addr, int timeout, NetworkInterface netif, int ttl) throws IOException {
+ // Android-changed: rewritten on the top of IoBridge and Libcore.os.
+ InetAddress sourceAddr = null;
+ if (netif != null) {
+ /*
+ * Let's make sure we bind to an address of the proper family.
+ * Which means same family as addr because at this point it could
+ * be either an IPv6 address or an IPv4 address (case of a dual
+ * stack system).
+ */
+ java.util.Enumeration<InetAddress> it = netif.getInetAddresses();
+ InetAddress inetaddr = null;
+ while (it.hasMoreElements()) {
+ inetaddr = it.nextElement();
+ if (inetaddr.getClass().isInstance(addr)) {
+ sourceAddr = inetaddr;
+ break;
+ }
+ }
+
+ if (sourceAddr == null) {
+ // Interface doesn't support the address family of
+ // the destination
+ return false;
+ }
+ }
+
+ // Android-changed: http://b/36933260 Implement root-less ICMP for isReachable().
+ /*
+ if (addr instanceof Inet6Address)
+ scope = ((Inet6Address) addr).getScopeId();
+ return isReachable0(addr.getAddress(), scope, timeout, ifaddr, ttl, netif_scope);
+ */
+ // Try ICMP first
+ if (icmpEcho(addr, timeout, sourceAddr, ttl)) {
+ return true;
+ }
+
+ // No good, let's fall back to TCP
+ return tcpEcho(addr, timeout, sourceAddr, ttl);
+ }
+
+ // BEGIN Android-added: http://b/36933260 Implement root-less ICMP for isReachable().
+ private boolean tcpEcho(InetAddress addr, int timeout, InetAddress sourceAddr, int ttl)
+ throws IOException {
+ FileDescriptor fd = null;
+ try {
+ fd = IoBridge.socket(AF_INET6, SOCK_STREAM, 0);
+ if (ttl > 0) {
+ IoBridge.setSocketOption(fd, IoBridge.JAVA_IP_TTL, ttl);
+ }
+ if (sourceAddr != null) {
+ IoBridge.bind(fd, sourceAddr, 0);
+ }
+ IoBridge.connect(fd, addr, 7 /* Echo-protocol port */, timeout);
+ return true;
+ } catch (IOException e) {
+ // Connection refused by remote (ECONNREFUSED) implies reachable. Otherwise silently
+ // ignore the exception and return false.
+ Throwable cause = e.getCause();
+ return cause instanceof ErrnoException
+ && ((ErrnoException) cause).errno == ECONNREFUSED;
+ } finally {
+ IoBridge.closeAndSignalBlockedThreads(fd);
+ }
+ }
+
+ protected boolean icmpEcho(InetAddress addr, int timeout, InetAddress sourceAddr, int ttl)
+ throws IOException {
+
+ FileDescriptor fd = null;
+ try {
+ boolean isIPv4 = addr instanceof Inet4Address;
+ int domain = isIPv4 ? AF_INET : AF_INET6;
+ int icmpProto = isIPv4 ? IPPROTO_ICMP : IPPROTO_ICMPV6;
+ fd = IoBridge.socket(domain, SOCK_DGRAM, icmpProto);
+
+ if (ttl > 0) {
+ IoBridge.setSocketOption(fd, IoBridge.JAVA_IP_TTL, ttl);
+ }
+ if (sourceAddr != null) {
+ IoBridge.bind(fd, sourceAddr, 0);
+ }
+
+ byte[] packet;
+
+ // ICMP is unreliable, try sending requests every second until timeout.
+ for (int to = timeout, seq = 1; to > 0; ++seq) {
+ int sockTo = to >= 1000 ? 1000 : to;
+
+ IoBridge.setSocketOption(fd, SocketOptions.SO_TIMEOUT, sockTo);
+
+ packet = IcmpHeaders.createIcmpEchoHdr(isIPv4, seq);
+ IoBridge.sendto(fd, packet, 0, packet.length, 0, addr, 0);
+ final int icmpId = IoBridge.getLocalInetSocketAddress(fd).getPort();
+
+ byte[] received = new byte[packet.length];
+ DatagramPacket receivedPacket = new DatagramPacket(received, packet.length);
+ int size = IoBridge
+ .recvfrom(true, fd, received, 0, received.length, 0, receivedPacket, false);
+ if (size == packet.length) {
+ byte expectedType = isIPv4 ? (byte) ICMP_ECHOREPLY
+ : (byte) ICMP6_ECHO_REPLY;
+ if (receivedPacket.getAddress().equals(addr)
+ && received[0] == expectedType
+ && received[4] == (byte) (icmpId >> 8)
+ && received[5] == (byte) icmpId) {
+ int receivedSequence = ((received[6] & 0xff) << 8) + (received[7] & 0xff);
+ if (receivedSequence <= seq) {
+ return true;
+ }
+ }
+ }
+ to -= sockTo;
+ }
+ } catch (IOException e) {
+ // Silently ignore and fall back.
+ } finally {
+ if (fd != null) {
+ try {
+ Libcore.os.close(fd);
+ } catch (ErrnoException e) { }
+ }
+ }
+
+ return false;
+ }
+ // END Android-added: http://b/36933260 Implement root-less ICMP for isReachable().
+
+ // BEGIN Android-changed: Let anyLocalAddress() always return an IPv6 address.
+ @Override
+ public InetAddress anyLocalAddress() {
+ synchronized (Inet6AddressImpl.class) {
+ // We avoid initializing anyLocalAddress during <clinit> to avoid issues
+ // caused by the dependency chains of these classes. InetAddress depends on
+ // InetAddressImpl, but Inet6Address & Inet4Address are its subclasses.
+ // Also see {@code loopbackAddresses). http://b/26700324
+ if (anyLocalAddress == null) {
+ Inet6Address anyAddress = new Inet6Address();
+ anyAddress.holder().hostName = "::";
+ anyLocalAddress = anyAddress;
+ }
+
+ return anyLocalAddress;
+ }
+ }
+ // END Android-changed: Let anyLocalAddress() always return an IPv6 address.
+
+ // BEGIN Android-changed: Let loopbackAddresses() return both Inet4 and Inet6 loopbacks.
+ @Override
+ public InetAddress[] loopbackAddresses() {
+ synchronized (Inet6AddressImpl.class) {
+ // We avoid initializing anyLocalAddress during <clinit> to avoid issues
+ // caused by the dependency chains of these classes. InetAddress depends on
+ // InetAddressImpl, but Inet6Address & Inet4Address are its subclasses.
+ // Also see {@code anyLocalAddress).
+ if (loopbackAddresses == null) {
+ loopbackAddresses = new InetAddress[]{Inet6Address.LOOPBACK, Inet4Address.LOOPBACK};
+ }
+
+ return loopbackAddresses;
+ }
+ }
+ // END Android-changed: Let loopbackAddresses() return both Inet4 and Inet6 loopbacks.
+
+ // BEGIN Android-changed: b/28609551 Rewrite getHostByAddr0 using POSIX library Libcore.os.
+ private String getHostByAddr0(byte[] addr) throws UnknownHostException {
+ // Android-changed: Rewritten on the top of Libcore.os
+ InetAddress hostaddr = InetAddress.getByAddress(addr);
+ try {
+ return Libcore.os.getnameinfo(hostaddr, NI_NAMEREQD);
+ } catch (GaiException e) {
+ UnknownHostException uhe = new UnknownHostException(hostaddr.toString());
+ uhe.initCause(e);
+ throw uhe;
+ }
+ }
+ // END Android-changed: b/28609551 Rewrite getHostByAddr0 using POSIX library Libcore.os.
+}
diff --git a/java/net/InetAddress.annotated.java b/java/net/InetAddress.annotated.java
new file mode 100644
index 0000000..6f4492d
--- /dev/null
+++ b/java/net/InetAddress.annotated.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package java.net;
+
+import java.io.ObjectStreamException;
+import java.io.IOException;
+
+@SuppressWarnings({"unchecked", "deprecation", "all"})
+public class InetAddress implements java.io.Serializable {
+
+InetAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isMulticastAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isAnyLocalAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isLoopbackAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isLinkLocalAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isSiteLocalAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCGlobal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCNodeLocal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCLinkLocal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCSiteLocal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCOrgLocal() { throw new RuntimeException("Stub!"); }
+
+public boolean isReachable(int timeout) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public boolean isReachable(java.net.NetworkInterface netif, int ttl, int timeout) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public boolean isReachableByICMP(int timeout) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public java.lang.String getHostName() { throw new RuntimeException("Stub!"); }
+
+public java.lang.String getCanonicalHostName() { throw new RuntimeException("Stub!"); }
+
+public byte[] getAddress() { throw new RuntimeException("Stub!"); }
+
+public java.lang.String getHostAddress() { throw new RuntimeException("Stub!"); }
+
+public int hashCode() { throw new RuntimeException("Stub!"); }
+
+public boolean equals(java.lang.Object obj) { throw new RuntimeException("Stub!"); }
+
+public java.lang.String toString() { throw new RuntimeException("Stub!"); }
+
+public static java.net.InetAddress getByAddress(java.lang.String host, byte[] addr) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+
+public static java.net.InetAddress getByName(java.lang.String host) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+
+public static java.net.InetAddress[] getAllByName(java.lang.String host) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+
+public static java.net.InetAddress getLoopbackAddress() { throw new RuntimeException("Stub!"); }
+
+public static java.net.InetAddress getByAddress(byte[] addr) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+
+public static java.net.InetAddress getLocalHost() throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+
[email protected]
+public static boolean isNumeric(java.lang.String address) { throw new RuntimeException("Stub!"); }
+
[email protected]
+public static java.net.InetAddress parseNumericAddress(java.lang.String numericAddress) { throw new RuntimeException("Stub!"); }
+
[email protected]
+public static void clearDnsCache() { throw new RuntimeException("Stub!"); }
+
[email protected]
+public static java.net.InetAddress getByNameOnNet(java.lang.String host, int netId) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+
[email protected]
+public static java.net.InetAddress[] getAllByNameOnNet(java.lang.String host, int netId) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+}
+
diff --git a/java/net/InetAddress.java b/java/net/InetAddress.java
new file mode 100644
index 0000000..e4f8660
--- /dev/null
+++ b/java/net/InetAddress.java
@@ -0,0 +1,1702 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.ObjectStreamException;
+import java.io.ObjectStreamField;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectInputStream.GetField;
+import java.io.ObjectOutputStream;
+import java.io.ObjectOutputStream.PutField;
+import libcore.net.InetAddressUtils;
+import sun.net.util.IPAddressUtil;
+import sun.net.spi.nameservice.*;
+import libcore.io.Libcore;
+
+/**
+ * This class represents an Internet Protocol (IP) address.
+ *
+ * <p> An IP address is either a 32-bit or 128-bit unsigned number
+ * used by IP, a lower-level protocol on which protocols like UDP and
+ * TCP are built. The IP address architecture is defined by <a
+ * href="http://www.ietf.org/rfc/rfc790.txt"><i>RFC 790:
+ * Assigned Numbers</i></a>, <a
+ * href="http://www.ietf.org/rfc/rfc1918.txt"> <i>RFC 1918:
+ * Address Allocation for Private Internets</i></a>, <a
+ * href="http://www.ietf.org/rfc/rfc2365.txt"><i>RFC 2365:
+ * Administratively Scoped IP Multicast</i></a>, and <a
+ * href="http://www.ietf.org/rfc/rfc2373.txt"><i>RFC 2373: IP
+ * Version 6 Addressing Architecture</i></a>. An instance of an
+ * InetAddress consists of an IP address and possibly its
+ * corresponding host name (depending on whether it is constructed
+ * with a host name or whether it has already done reverse host name
+ * resolution).
+ *
+ * <h3> Address types </h3>
+ *
+ * <blockquote><table cellspacing=2 summary="Description of unicast and multicast address types">
+ * <tr><th valign=top><i>unicast</i></th>
+ * <td>An identifier for a single interface. A packet sent to
+ * a unicast address is delivered to the interface identified by
+ * that address.
+ *
+ * <p> The Unspecified Address -- Also called anylocal or wildcard
+ * address. It must never be assigned to any node. It indicates the
+ * absence of an address. One example of its use is as the target of
+ * bind, which allows a server to accept a client connection on any
+ * interface, in case the server host has multiple interfaces.
+ *
+ * <p> The <i>unspecified</i> address must not be used as
+ * the destination address of an IP packet.
+ *
+ * <p> The <i>Loopback</i> Addresses -- This is the address
+ * assigned to the loopback interface. Anything sent to this
+ * IP address loops around and becomes IP input on the local
+ * host. This address is often used when testing a
+ * client.</td></tr>
+ * <tr><th valign=top><i>multicast</i></th>
+ * <td>An identifier for a set of interfaces (typically belonging
+ * to different nodes). A packet sent to a multicast address is
+ * delivered to all interfaces identified by that address.</td></tr>
+ * </table></blockquote>
+ *
+ * <h4> IP address scope </h4>
+ *
+ * <p> <i>Link-local</i> addresses are designed to be used for addressing
+ * on a single link for purposes such as auto-address configuration,
+ * neighbor discovery, or when no routers are present.
+ *
+ * <p> <i>Site-local</i> addresses are designed to be used for addressing
+ * inside of a site without the need for a global prefix.
+ *
+ * <p> <i>Global</i> addresses are unique across the internet.
+ *
+ * <h4> Textual representation of IP addresses </h4>
+ *
+ * The textual representation of an IP address is address family specific.
+ *
+ * <p>
+ *
+ * For IPv4 address format, please refer to <A
+ * HREF="Inet4Address.html#format">Inet4Address#format</A>; For IPv6
+ * address format, please refer to <A
+ * HREF="Inet6Address.html#format">Inet6Address#format</A>.
+ *
+ * <P>There is a <a href="doc-files/net-properties.html#Ipv4IPv6">couple of
+ * System Properties</a> affecting how IPv4 and IPv6 addresses are used.</P>
+ *
+ * <h4> Host Name Resolution </h4>
+ *
+ * Host name-to-IP address <i>resolution</i> is accomplished through
+ * the use of a combination of local machine configuration information
+ * and network naming services such as the Domain Name System (DNS)
+ * and Network Information Service(NIS). The particular naming
+ * services(s) being used is by default the local machine configured
+ * one. For any host name, its corresponding IP address is returned.
+ *
+ * <p> <i>Reverse name resolution</i> means that for any IP address,
+ * the host associated with the IP address is returned.
+ *
+ * <p> The InetAddress class provides methods to resolve host names to
+ * their IP addresses and vice versa.
+ *
+ * <h4> InetAddress Caching </h4>
+ *
+ * The InetAddress class has a cache to store successful as well as
+ * unsuccessful host name resolutions.
+ *
+ * <p> By default, when a security manager is installed, in order to
+ * protect against DNS spoofing attacks,
+ * the result of positive host name resolutions are
+ * cached forever. When a security manager is not installed, the default
+ * behavior is to cache entries for a finite (implementation dependent)
+ * period of time. The result of unsuccessful host
+ * name resolution is cached for a very short period of time (10
+ * seconds) to improve performance.
+ *
+ * <p> If the default behavior is not desired, then a Java security property
+ * can be set to a different Time-to-live (TTL) value for positive
+ * caching. Likewise, a system admin can configure a different
+ * negative caching TTL value when needed.
+ *
+ * <p> Two Java security properties control the TTL values used for
+ * positive and negative host name resolution caching:
+ *
+ * <blockquote>
+ * <dl>
+ * <dt><b>networkaddress.cache.ttl</b></dt>
+ * <dd>Indicates the caching policy for successful name lookups from
+ * the name service. The value is specified as as integer to indicate
+ * the number of seconds to cache the successful lookup. The default
+ * setting is to cache for an implementation specific period of time.
+ * <p>
+ * A value of -1 indicates "cache forever".
+ * </dd>
+ * <dt><b>networkaddress.cache.negative.ttl</b> (default: 10)</dt>
+ * <dd>Indicates the caching policy for un-successful name lookups
+ * from the name service. The value is specified as as integer to
+ * indicate the number of seconds to cache the failure for
+ * un-successful lookups.
+ * <p>
+ * A value of 0 indicates "never cache".
+ * A value of -1 indicates "cache forever".
+ * </dd>
+ * </dl>
+ * </blockquote>
+ *
+ * @author Chris Warth
+ * @see java.net.InetAddress#getByAddress(byte[])
+ * @see java.net.InetAddress#getByAddress(java.lang.String, byte[])
+ * @see java.net.InetAddress#getAllByName(java.lang.String)
+ * @see java.net.InetAddress#getByName(java.lang.String)
+ * @see java.net.InetAddress#getLocalHost()
+ * @since JDK1.0
+ */
+public
+class InetAddress implements java.io.Serializable {
+ // BEGIN Android-removed: Android uses linux-based OsConstants.
+ /*
+ * Specify the address family: Internet Protocol, Version 4
+ * @since 1.4
+ *
+ static final int IPv4 = 1;
+
+ /**
+ * Specify the address family: Internet Protocol, Version 6
+ * @since 1.4
+ *
+ static final int IPv6 = 2;
+ */
+ // END Android-removed: Android uses linux-based OsConstants.
+
+ // Android-removed: Android doesn't support the preference.
+ // /* Specify address family preference */
+ //static transient boolean preferIPv6Address = false;
+
+ static class InetAddressHolder {
+ /**
+ * Reserve the original application specified hostname.
+ *
+ * The original hostname is useful for domain-based endpoint
+ * identification (see RFC 2818 and RFC 6125). If an address
+ * was created with a raw IP address, a reverse name lookup
+ * may introduce endpoint identification security issue via
+ * DNS forging.
+ *
+ * Oracle JSSE provider is using this original hostname, via
+ * sun.misc.JavaNetAccess, for SSL/TLS endpoint identification.
+ *
+ * Note: May define a new public method in the future if necessary.
+ */
+ String originalHostName;
+
+ InetAddressHolder() {}
+
+ InetAddressHolder(String hostName, int address, int family) {
+ this.originalHostName = hostName;
+ this.hostName = hostName;
+ this.address = address;
+ this.family = family;
+ }
+
+ void init(String hostName, int family) {
+ this.originalHostName = hostName;
+ this.hostName = hostName;
+ if (family != -1) {
+ this.family = family;
+ }
+ }
+
+ String hostName;
+
+ String getHostName() {
+ return hostName;
+ }
+
+ String getOriginalHostName() {
+ return originalHostName;
+ }
+
+ /**
+ * Holds a 32-bit IPv4 address.
+ */
+ int address;
+
+ int getAddress() {
+ return address;
+ }
+
+ // Android-changed: Documentation: use Linux-based OsConstants.
+ /**
+ * Specifies the address family type, for instance, AF_INET for IPv4
+ * addresses, and AF_INET6 for IPv6 addresses.
+ */
+ int family;
+
+ int getFamily() {
+ return family;
+ }
+ }
+
+ transient InetAddressHolder holder;
+
+ InetAddressHolder holder() {
+ return holder;
+ }
+
+ /* The implementation is always dual stack IPv6/IPv4 on android */
+ static final InetAddressImpl impl = new Inet6AddressImpl();
+
+ /* Used to store the name service provider */
+ // Android-changed: Android has only one name service.
+ // Android doesn't allow user to provide custom name services.
+ // private static List<NameService> nameServices = null;
+ private static final NameService nameService = new NameService() {
+ public InetAddress[] lookupAllHostAddr(String host, int netId)
+ throws UnknownHostException {
+ return impl.lookupAllHostAddr(host, netId);
+ }
+ public String getHostByAddr(byte[] addr)
+ throws UnknownHostException {
+ return impl.getHostByAddr(addr);
+ }
+ };
+
+ /* Used to store the best available hostname */
+ private transient String canonicalHostName = null;
+
+ /** use serialVersionUID from JDK 1.0.2 for interoperability */
+ private static final long serialVersionUID = 3286316764910316507L;
+
+
+ // BEGIN Android-removed: Android doesn't need to load native library.
+ /*
+ * Load net library into runtime, and perform initializations.
+ *
+ static {
+ preferIPv6Address = java.security.AccessController.doPrivileged(
+ new GetBooleanAction("java.net.preferIPv6Addresses")).booleanValue();
+ AccessController.doPrivileged(
+ new java.security.PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("net");
+ return null;
+ }
+ });
+ init();
+ }
+ */
+ // END Android-removed: Android doesn't need to load native library.
+
+ /**
+ * Constructor for the Socket.accept() method.
+ * This creates an empty InetAddress, which is filled in by
+ * the accept() method. This InetAddress, however, is not
+ * put in the address cache, since it is not created by name.
+ */
+ InetAddress() {
+ holder = new InetAddressHolder();
+ }
+
+ /**
+ * Replaces the de-serialized object with an Inet4Address object.
+ *
+ * @return the alternate object to the de-serialized object.
+ *
+ * @throws ObjectStreamException if a new object replacing this
+ * object could not be created
+ */
+ private Object readResolve() throws ObjectStreamException {
+ // will replace the deserialized 'this' object
+ return new Inet4Address(holder().getHostName(), holder().getAddress());
+ }
+
+ /**
+ * Utility routine to check if the InetAddress is an
+ * IP multicast address.
+ * @return a {@code boolean} indicating if the InetAddress is
+ * an IP multicast address
+ * @since JDK1.1
+ */
+ public boolean isMulticastAddress() {
+ return false;
+ }
+
+ /**
+ * Utility routine to check if the InetAddress in a wildcard address.
+ * @return a {@code boolean} indicating if the Inetaddress is
+ * a wildcard address.
+ * @since 1.4
+ */
+ public boolean isAnyLocalAddress() {
+ return false;
+ }
+
+ /**
+ * Utility routine to check if the InetAddress is a loopback address.
+ *
+ * @return a {@code boolean} indicating if the InetAddress is
+ * a loopback address; or false otherwise.
+ * @since 1.4
+ */
+ public boolean isLoopbackAddress() {
+ return false;
+ }
+
+ /**
+ * Utility routine to check if the InetAddress is an link local address.
+ *
+ * @return a {@code boolean} indicating if the InetAddress is
+ * a link local address; or false if address is not a link local unicast address.
+ * @since 1.4
+ */
+ public boolean isLinkLocalAddress() {
+ return false;
+ }
+
+ /**
+ * Utility routine to check if the InetAddress is a site local address.
+ *
+ * @return a {@code boolean} indicating if the InetAddress is
+ * a site local address; or false if address is not a site local unicast address.
+ * @since 1.4
+ */
+ public boolean isSiteLocalAddress() {
+ return false;
+ }
+
+ /**
+ * Utility routine to check if the multicast address has global scope.
+ *
+ * @return a {@code boolean} indicating if the address has
+ * is a multicast address of global scope, false if it is not
+ * of global scope or it is not a multicast address
+ * @since 1.4
+ */
+ public boolean isMCGlobal() {
+ return false;
+ }
+
+ /**
+ * Utility routine to check if the multicast address has node scope.
+ *
+ * @return a {@code boolean} indicating if the address has
+ * is a multicast address of node-local scope, false if it is not
+ * of node-local scope or it is not a multicast address
+ * @since 1.4
+ */
+ public boolean isMCNodeLocal() {
+ return false;
+ }
+
+ /**
+ * Utility routine to check if the multicast address has link scope.
+ *
+ * @return a {@code boolean} indicating if the address has
+ * is a multicast address of link-local scope, false if it is not
+ * of link-local scope or it is not a multicast address
+ * @since 1.4
+ */
+ public boolean isMCLinkLocal() {
+ return false;
+ }
+
+ /**
+ * Utility routine to check if the multicast address has site scope.
+ *
+ * @return a {@code boolean} indicating if the address has
+ * is a multicast address of site-local scope, false if it is not
+ * of site-local scope or it is not a multicast address
+ * @since 1.4
+ */
+ public boolean isMCSiteLocal() {
+ return false;
+ }
+
+ /**
+ * Utility routine to check if the multicast address has organization scope.
+ *
+ * @return a {@code boolean} indicating if the address has
+ * is a multicast address of organization-local scope,
+ * false if it is not of organization-local scope
+ * or it is not a multicast address
+ * @since 1.4
+ */
+ public boolean isMCOrgLocal() {
+ return false;
+ }
+
+
+ // Android-changed: Document that impl tries ICMP ECHO REQUESTs first.
+ // The sole implementation, Inet6AddressImpl.isReachable(), tries ICMP ECHO REQUESTs before
+ // TCP ECHO REQUESTs on Android. On Android, these are both possible without root access.
+ /**
+ * Test whether that address is reachable. Best effort is made by the
+ * implementation to try to reach the host, but firewalls and server
+ * configuration may block requests resulting in a unreachable status
+ * while some specific ports may be accessible.
+ * <p>
+ * Android implementation attempts ICMP ECHO REQUESTs first, on failure it
+ * will fall back to TCP ECHO REQUESTs. Success on either protocol will
+ * return true.
+ * <p>
+ * The timeout value, in milliseconds, indicates the maximum amount of time
+ * the try should take. If the operation times out before getting an
+ * answer, the host is deemed unreachable. A negative value will result
+ * in an IllegalArgumentException being thrown.
+ *
+ * @param timeout the time, in milliseconds, before the call aborts
+ * @return a {@code boolean} indicating if the address is reachable.
+ * @throws IOException if a network error occurs
+ * @throws IllegalArgumentException if {@code timeout} is negative.
+ * @since 1.5
+ */
+ public boolean isReachable(int timeout) throws IOException {
+ return isReachable(null, 0 , timeout);
+ }
+
+ // Android-changed: Document that impl tries ICMP ECHO REQUESTs first.
+ // The sole implementation, Inet6AddressImpl.isReachable(), tries ICMP ECHO REQUESTs before
+ // TCP ECHO REQUESTs on Android. On Android, these are both possible without root access.
+ /**
+ * Test whether that address is reachable. Best effort is made by the
+ * implementation to try to reach the host, but firewalls and server
+ * configuration may block requests resulting in a unreachable status
+ * while some specific ports may be accessible.
+ * <p>
+ * Android implementation attempts ICMP ECHO REQUESTs first, on failure it
+ * will fall back to TCP ECHO REQUESTs. Success on either protocol will
+ * return true.
+ * <p>
+ * The {@code network interface} and {@code ttl} parameters
+ * let the caller specify which network interface the test will go through
+ * and the maximum number of hops the packets should go through.
+ * A negative value for the {@code ttl} will result in an
+ * IllegalArgumentException being thrown.
+ * <p>
+ * The timeout value, in milliseconds, indicates the maximum amount of time
+ * the try should take. If the operation times out before getting an
+ * answer, the host is deemed unreachable. A negative value will result
+ * in an IllegalArgumentException being thrown.
+ *
+ * @param netif the NetworkInterface through which the
+ * test will be done, or null for any interface
+ * @param ttl the maximum numbers of hops to try or 0 for the
+ * default
+ * @param timeout the time, in milliseconds, before the call aborts
+ * @throws IllegalArgumentException if either {@code timeout}
+ * or {@code ttl} are negative.
+ * @return a {@code boolean}indicating if the address is reachable.
+ * @throws IOException if a network error occurs
+ * @since 1.5
+ */
+ public boolean isReachable(NetworkInterface netif, int ttl,
+ int timeout) throws IOException {
+ if (ttl < 0)
+ throw new IllegalArgumentException("ttl can't be negative");
+ if (timeout < 0)
+ throw new IllegalArgumentException("timeout can't be negative");
+
+ return impl.isReachable(this, timeout, netif, ttl);
+ }
+
+ // BEGIN Android-added: isReachableByICMP(timeout).
+ /**
+ * @hide For testing only
+ */
+ public boolean isReachableByICMP(int timeout) throws IOException {
+ return ((Inet6AddressImpl) impl).icmpEcho(this, timeout, null, 0);
+ }
+ // END Android-added: isReachableByICMP(timeout).
+
+ /**
+ * Gets the host name for this IP address.
+ *
+ * <p>If this InetAddress was created with a host name,
+ * this host name will be remembered and returned;
+ * otherwise, a reverse name lookup will be performed
+ * and the result will be returned based on the system
+ * configured name lookup service. If a lookup of the name service
+ * is required, call
+ * {@link #getCanonicalHostName() getCanonicalHostName}.
+ *
+ * <p>If there is a security manager, its
+ * {@code checkConnect} method is first called
+ * with the hostname and {@code -1}
+ * as its arguments to see if the operation is allowed.
+ * If the operation is not allowed, it will return
+ * the textual representation of the IP address.
+ *
+ * @return the host name for this IP address, or if the operation
+ * is not allowed by the security check, the textual
+ * representation of the IP address.
+ *
+ * @see InetAddress#getCanonicalHostName
+ * @see SecurityManager#checkConnect
+ */
+ public String getHostName() {
+ // Android-changed: Remove SecurityManager check.
+ if (holder().getHostName() == null) {
+ holder().hostName = InetAddress.getHostFromNameService(this);
+ }
+ return holder().getHostName();
+ }
+
+ // BEGIN Android-removed: Android doesn't support SecurityManager.
+ /*
+ * Returns the hostname for this address.
+ * If the host is equal to null, then this address refers to any
+ * of the local machine's available network addresses.
+ * this is package private so SocketPermission can make calls into
+ * here without a security check.
+ *
+ * <p>If there is a security manager, this method first
+ * calls its {@code checkConnect} method
+ * with the hostname and {@code -1}
+ * as its arguments to see if the calling code is allowed to know
+ * the hostname for this IP address, i.e., to connect to the host.
+ * If the operation is not allowed, it will return
+ * the textual representation of the IP address.
+ *
+ * @return the host name for this IP address, or if the operation
+ * is not allowed by the security check, the textual
+ * representation of the IP address.
+ *
+ * @param check make security check if true
+ *
+ * @see SecurityManager#checkConnect
+ *
+ String getHostName(boolean check) {
+ if (holder().getHostName() == null) {
+ holder().hostName = InetAddress.getHostFromNameService(this, check);
+ }
+ return holder().getHostName();
+ }
+ */
+ // END Android-removed: Android doesn't support SecurityManager.
+
+ /**
+ * Gets the fully qualified domain name for this IP address.
+ * Best effort method, meaning we may not be able to return
+ * the FQDN depending on the underlying system configuration.
+ *
+ * <p>If there is a security manager, this method first
+ * calls its {@code checkConnect} method
+ * with the hostname and {@code -1}
+ * as its arguments to see if the calling code is allowed to know
+ * the hostname for this IP address, i.e., to connect to the host.
+ * If the operation is not allowed, it will return
+ * the textual representation of the IP address.
+ *
+ * @return the fully qualified domain name for this IP address,
+ * or if the operation is not allowed by the security check,
+ * the textual representation of the IP address.
+ *
+ * @see SecurityManager#checkConnect
+ *
+ * @since 1.4
+ */
+ public String getCanonicalHostName() {
+ // Android-changed: Remove SecurityManager check.
+ if (canonicalHostName == null) {
+ canonicalHostName = InetAddress.getHostFromNameService(this);
+ }
+ return canonicalHostName;
+ }
+
+ // Android-changed: Remove SecurityManager check.
+ // * @param check make security check if true
+ /**
+ * Returns the hostname for this address.
+ *
+ * <p>If there is a security manager, this method first
+ * calls its {@code checkConnect} method
+ * with the hostname and {@code -1}
+ * as its arguments to see if the calling code is allowed to know
+ * the hostname for this IP address, i.e., to connect to the host.
+ * If the operation is not allowed, it will return
+ * the textual representation of the IP address.
+ *
+ * @return the host name for this IP address, or if the operation
+ * is not allowed by the security check, the textual
+ * representation of the IP address.
+ *
+ * @see SecurityManager#checkConnect
+ */
+ private static String getHostFromNameService(InetAddress addr) {
+ String host = null;
+ try {
+ // first lookup the hostname
+ // Android-changed: Android has only one name service.
+ host = nameService.getHostByAddr(addr.getAddress());
+
+ /* now get all the IP addresses for this hostname,
+ * and make sure one of them matches the original IP
+ * address. We do this to try and prevent spoofing.
+ */
+ InetAddress[] arr = nameService.lookupAllHostAddr(host, NETID_UNSET);
+ boolean ok = false;
+
+ if (arr != null) {
+ for(int i = 0; !ok && i < arr.length; i++) {
+ ok = addr.equals(arr[i]);
+ }
+ }
+
+ //XXX: if it looks a spoof just return the address?
+ if (!ok) {
+ host = addr.getHostAddress();
+ return host;
+ }
+ } catch (UnknownHostException e) {
+ host = addr.getHostAddress();
+ }
+
+ return host;
+ }
+
+ /**
+ * Returns the raw IP address of this {@code InetAddress}
+ * object. The result is in network byte order: the highest order
+ * byte of the address is in {@code getAddress()[0]}.
+ *
+ * @return the raw IP address of this object.
+ */
+ public byte[] getAddress() {
+ return null;
+ }
+
+ /**
+ * Returns the IP address string in textual presentation.
+ *
+ * @return the raw IP address in a string format.
+ * @since JDK1.0.2
+ */
+ public String getHostAddress() {
+ return null;
+ }
+
+ /**
+ * Returns a hashcode for this IP address.
+ *
+ * @return a hash code value for this IP address.
+ */
+ public int hashCode() {
+ return -1;
+ }
+
+ /**
+ * Compares this object against the specified object.
+ * The result is {@code true} if and only if the argument is
+ * not {@code null} and it represents the same IP address as
+ * this object.
+ * <p>
+ * Two instances of {@code InetAddress} represent the same IP
+ * address if the length of the byte arrays returned by
+ * {@code getAddress} is the same for both, and each of the
+ * array components is the same for the byte arrays.
+ *
+ * @param obj the object to compare against.
+ * @return {@code true} if the objects are the same;
+ * {@code false} otherwise.
+ * @see java.net.InetAddress#getAddress()
+ */
+ public boolean equals(Object obj) {
+ return false;
+ }
+
+ /**
+ * Converts this IP address to a {@code String}. The
+ * string returned is of the form: hostname / literal IP
+ * address.
+ *
+ * If the host name is unresolved, no reverse name service lookup
+ * is performed. The hostname part will be represented by an empty string.
+ *
+ * @return a string representation of this IP address.
+ */
+ public String toString() {
+ String hostName = holder().getHostName();
+ return ((hostName != null) ? hostName : "")
+ + "/" + getHostAddress();
+ }
+
+ // BEGIN Android-removed: Resolves a hostname using Libcore.os.
+ /*
+ * Cached addresses - our own litle nis, not!
+ *
+ private static Cache addressCache = new Cache(Cache.Type.Positive);
+
+ private static Cache negativeCache = new Cache(Cache.Type.Negative);
+
+ private static boolean addressCacheInit = false;
+
+ static InetAddress[] unknown_array; // put THIS in cache
+
+ static InetAddressImpl impl;
+
+ private static final HashMap<String, Void> lookupTable = new HashMap<>();
+
+ /**
+ * Represents a cache entry
+ *
+ static final class CacheEntry {
+
+ CacheEntry(InetAddress[] addresses, long expiration) {
+ this.addresses = addresses;
+ this.expiration = expiration;
+ }
+
+ InetAddress[] addresses;
+ long expiration;
+ }
+
+ /**
+ * A cache that manages entries based on a policy specified
+ * at creation time.
+ *
+ static final class Cache {
+ private LinkedHashMap<String, CacheEntry> cache;
+ private Type type;
+
+ enum Type {Positive, Negative};
+
+ /**
+ * Create cache
+ *
+ public Cache(Type type) {
+ this.type = type;
+ cache = new LinkedHashMap<String, CacheEntry>();
+ }
+
+ private int getPolicy() {
+ if (type == Type.Positive) {
+ return InetAddressCachePolicy.get();
+ } else {
+ return InetAddressCachePolicy.getNegative();
+ }
+ }
+
+ /**
+ * Add an entry to the cache. If there's already an
+ * entry then for this host then the entry will be
+ * replaced.
+ *
+ public Cache put(String host, InetAddress[] addresses) {
+ int policy = getPolicy();
+ if (policy == InetAddressCachePolicy.NEVER) {
+ return this;
+ }
+
+ // purge any expired entries
+
+ if (policy != InetAddressCachePolicy.FOREVER) {
+
+ // As we iterate in insertion order we can
+ // terminate when a non-expired entry is found.
+ LinkedList<String> expired = new LinkedList<>();
+ long now = System.currentTimeMillis();
+ for (String key : cache.keySet()) {
+ CacheEntry entry = cache.get(key);
+
+ if (entry.expiration >= 0 && entry.expiration < now) {
+ expired.add(key);
+ } else {
+ break;
+ }
+ }
+
+ for (String key : expired) {
+ cache.remove(key);
+ }
+ }
+
+ // create new entry and add it to the cache
+ // -- as a HashMap replaces existing entries we
+ // don't need to explicitly check if there is
+ // already an entry for this host.
+ long expiration;
+ if (policy == InetAddressCachePolicy.FOREVER) {
+ expiration = -1;
+ } else {
+ expiration = System.currentTimeMillis() + (policy * 1000);
+ }
+ CacheEntry entry = new CacheEntry(addresses, expiration);
+ cache.put(host, entry);
+ return this;
+ }
+
+ /**
+ * Query the cache for the specific host. If found then
+ * return its CacheEntry, or null if not found.
+ *
+ public CacheEntry get(String host) {
+ int policy = getPolicy();
+ if (policy == InetAddressCachePolicy.NEVER) {
+ return null;
+ }
+ CacheEntry entry = cache.get(host);
+
+ // check if entry has expired
+ if (entry != null && policy != InetAddressCachePolicy.FOREVER) {
+ if (entry.expiration >= 0 &&
+ entry.expiration < System.currentTimeMillis()) {
+ cache.remove(host);
+ entry = null;
+ }
+ }
+
+ return entry;
+ }
+ }
+
+ /*
+ * Initialize cache and insert anyLocalAddress into the
+ * unknown array with no expiry.
+ *
+ private static void cacheInitIfNeeded() {
+ assert Thread.holdsLock(addressCache);
+ if (addressCacheInit) {
+ return;
+ }
+ unknown_array = new InetAddress[1];
+ unknown_array[0] = impl.anyLocalAddress();
+
+ addressCache.put(impl.anyLocalAddress().getHostName(),
+ unknown_array);
+
+ addressCacheInit = true;
+ }
+
+ /*
+ * Cache the given hostname and addresses.
+ *
+ private static void cacheAddresses(String hostname,
+ InetAddress[] addresses,
+ boolean success) {
+ hostname = hostname.toLowerCase();
+ synchronized (addressCache) {
+ cacheInitIfNeeded();
+ if (success) {
+ addressCache.put(hostname, addresses);
+ } else {
+ negativeCache.put(hostname, addresses);
+ }
+ }
+ }
+
+ /*
+ * Lookup hostname in cache (positive & negative cache). If
+ * found return addresses, null if not found.
+ *
+ private static InetAddress[] getCachedAddresses(String hostname) {
+ hostname = hostname.toLowerCase();
+
+ // search both positive & negative caches
+
+ synchronized (addressCache) {
+ cacheInitIfNeeded();
+
+ CacheEntry entry = addressCache.get(hostname);
+ if (entry == null) {
+ entry = negativeCache.get(hostname);
+ }
+
+ if (entry != null) {
+ return entry.addresses;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ private static NameService createNSProvider(String provider) {
+ if (provider == null)
+ return null;
+
+ NameService nameService = null;
+ if (provider.equals("default")) {
+ // initialize the default name service
+ nameService = new NameService() {
+ public InetAddress[] lookupAllHostAddr(String host)
+ throws UnknownHostException {
+ return impl.lookupAllHostAddr(host);
+ }
+ public String getHostByAddr(byte[] addr)
+ throws UnknownHostException {
+ return impl.getHostByAddr(addr);
+ }
+ };
+ } else {
+ final String providerName = provider;
+ try {
+ nameService = java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedExceptionAction<NameService>() {
+ public NameService run() {
+ Iterator<NameServiceDescriptor> itr =
+ ServiceLoader.load(NameServiceDescriptor.class)
+ .iterator();
+ while (itr.hasNext()) {
+ NameServiceDescriptor nsd = itr.next();
+ if (providerName.
+ equalsIgnoreCase(nsd.getType()+","
+ +nsd.getProviderName())) {
+ try {
+ return nsd.createNameService();
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.err.println(
+ "Cannot create name service:"
+ +providerName+": " + e);
+ }
+ }
+ }
+
+ return null;
+ }
+ }
+ );
+ } catch (java.security.PrivilegedActionException e) {
+ }
+ }
+
+ return nameService;
+ }
+
+ static {
+ // create the impl
+ impl = InetAddressImplFactory.create();
+
+ // get name service if provided and requested
+ String provider = null;;
+ String propPrefix = "sun.net.spi.nameservice.provider.";
+ int n = 1;
+ nameServices = new ArrayList<NameService>();
+ provider = AccessController.doPrivileged(
+ new GetPropertyAction(propPrefix + n));
+ while (provider != null) {
+ NameService ns = createNSProvider(provider);
+ if (ns != null)
+ nameServices.add(ns);
+
+ n++;
+ provider = AccessController.doPrivileged(
+ new GetPropertyAction(propPrefix + n));
+ }
+
+ // if not designate any name services provider,
+ // create a default one
+ if (nameServices.size() == 0) {
+ NameService ns = createNSProvider("default");
+ nameServices.add(ns);
+ }
+ }
+ */
+ // END Android-removed: Resolves a hostname using Libcore.os.
+
+ /**
+ * Creates an InetAddress based on the provided host name and IP address.
+ * No name service is checked for the validity of the address.
+ *
+ * <p> The host name can either be a machine name, such as
+ * "{@code java.sun.com}", or a textual representation of its IP
+ * address.
+ * <p> No validity checking is done on the host name either.
+ *
+ * <p> If addr specifies an IPv4 address an instance of Inet4Address
+ * will be returned; otherwise, an instance of Inet6Address
+ * will be returned.
+ *
+ * <p> IPv4 address byte array must be 4 bytes long and IPv6 byte array
+ * must be 16 bytes long
+ *
+ * @param host the specified host
+ * @param addr the raw IP address in network byte order
+ * @return an InetAddress object created from the raw IP address.
+ * @exception UnknownHostException if IP address is of illegal length
+ * @since 1.4
+ */
+ public static InetAddress getByAddress(String host, byte[] addr) throws UnknownHostException {
+ return getByAddress(host, addr, -1 /* scopeId */);
+ }
+
+ // Android-added: Called by native code in Libcore.io.
+ // Do not delete. Called from native code.
+ private static InetAddress getByAddress(String host, byte[] addr, int scopeId)
+ throws UnknownHostException {
+ if (host != null && host.length() > 0 && host.charAt(0) == '[') {
+ if (host.charAt(host.length()-1) == ']') {
+ host = host.substring(1, host.length() -1);
+ }
+ }
+ if (addr != null) {
+ if (addr.length == Inet4Address.INADDRSZ) {
+ return new Inet4Address(host, addr);
+ } else if (addr.length == Inet6Address.INADDRSZ) {
+ byte[] newAddr
+ = IPAddressUtil.convertFromIPv4MappedAddress(addr);
+ if (newAddr != null) {
+ return new Inet4Address(host, newAddr);
+ } else {
+ return new Inet6Address(host, addr, scopeId);
+ }
+ }
+ }
+ throw new UnknownHostException("addr is of illegal length");
+ }
+
+
+ /**
+ * Determines the IP address of a host, given the host's name.
+ *
+ * <p> The host name can either be a machine name, such as
+ * "{@code java.sun.com}", or a textual representation of its
+ * IP address. If a literal IP address is supplied, only the
+ * validity of the address format is checked.
+ *
+ * <p> For {@code host} specified in literal IPv6 address,
+ * either the form defined in RFC 2732 or the literal IPv6 address
+ * format defined in RFC 2373 is accepted. IPv6 scoped addresses are also
+ * supported. See <a href="Inet6Address.html#scoped">here</a> for a description of IPv6
+ * scoped addresses.
+ *
+ * <p> If the host is {@code null} then an {@code InetAddress}
+ * representing an address of the loopback interface is returned.
+ * See <a href="http://www.ietf.org/rfc/rfc3330.txt">RFC 3330</a>
+ * section 2 and <a href="http://www.ietf.org/rfc/rfc2373.txt">RFC 2373</a>
+ * section 2.5.3. </p>
+ *
+ * @param host the specified host, or {@code null}.
+ * @return an IP address for the given host name.
+ * @exception UnknownHostException if no IP address for the
+ * {@code host} could be found, or if a scope_id was specified
+ * for a global IPv6 address.
+ * @exception SecurityException if a security manager exists
+ * and its checkConnect method doesn't allow the operation
+ */
+ public static InetAddress getByName(String host)
+ throws UnknownHostException {
+ // Android-changed: Rewritten on the top of Libcore.os.
+ return impl.lookupAllHostAddr(host, NETID_UNSET)[0];
+ }
+
+ /**
+ * Given the name of a host, returns an array of its IP addresses,
+ * based on the configured name service on the system.
+ *
+ * <p> The host name can either be a machine name, such as
+ * "{@code java.sun.com}", or a textual representation of its IP
+ * address. If a literal IP address is supplied, only the
+ * validity of the address format is checked.
+ *
+ * <p> For {@code host} specified in <i>literal IPv6 address</i>,
+ * either the form defined in RFC 2732 or the literal IPv6 address
+ * format defined in RFC 2373 is accepted. A literal IPv6 address may
+ * also be qualified by appending a scoped zone identifier or scope_id.
+ * The syntax and usage of scope_ids is described
+ * <a href="Inet6Address.html#scoped">here</a>.
+ * <p> If the host is {@code null} then an {@code InetAddress}
+ * representing an address of the loopback interface is returned.
+ * See <a href="http://www.ietf.org/rfc/rfc3330.txt">RFC 3330</a>
+ * section 2 and <a href="http://www.ietf.org/rfc/rfc2373.txt">RFC 2373</a>
+ * section 2.5.3. </p>
+ *
+ * <p> If there is a security manager and {@code host} is not
+ * null and {@code host.length() } is not equal to zero, the
+ * security manager's
+ * {@code checkConnect} method is called
+ * with the hostname and {@code -1}
+ * as its arguments to see if the operation is allowed.
+ *
+ * @param host the name of the host, or {@code null}.
+ * @return an array of all the IP addresses for a given host name.
+ *
+ * @exception UnknownHostException if no IP address for the
+ * {@code host} could be found, or if a scope_id was specified
+ * for a global IPv6 address.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkConnect} method doesn't allow the operation.
+ *
+ * @see SecurityManager#checkConnect
+ */
+ public static InetAddress[] getAllByName(String host)
+ throws UnknownHostException {
+ // Android-changed: Resolves a hostname using Libcore.os.
+ // Also, returns both the Inet4 and Inet6 loopback for null/empty host
+ return impl.lookupAllHostAddr(host, NETID_UNSET).clone();
+ }
+
+ /**
+ * Returns the loopback address.
+ * <p>
+ * The InetAddress returned will represent the IPv4
+ * loopback address, 127.0.0.1, or the IPv6 loopback
+ * address, ::1. The IPv4 loopback address returned
+ * is only one of many in the form 127.*.*.*
+ *
+ * @return the InetAddress loopback instance.
+ * @since 1.7
+ */
+ public static InetAddress getLoopbackAddress() {
+ // Android-changed: Always returns IPv6 loopback address in Android.
+ return impl.loopbackAddresses()[0];
+ }
+
+ // BEGIN Android-removed: Resolves a hostname using Libcore.os.
+ /*
+ * check if the literal address string has %nn appended
+ * returns -1 if not, or the numeric value otherwise.
+ *
+ * %nn may also be a string that represents the displayName of
+ * a currently available NetworkInterface.
+ *
+ private static int checkNumericZone (String s) throws UnknownHostException {
+ int percent = s.indexOf ('%');
+ int slen = s.length();
+ int digit, zone=0;
+ if (percent == -1) {
+ return -1;
+ }
+ for (int i=percent+1; i<slen; i++) {
+ char c = s.charAt(i);
+ if (c == ']') {
+ if (i == percent+1) {
+ /* empty per-cent field *
+ return -1;
+ }
+ break;
+ }
+ if ((digit = Character.digit (c, 10)) < 0) {
+ return -1;
+ }
+ zone = (zone * 10) + digit;
+ }
+ return zone;
+ }
+
+ private static InetAddress[] getAllByName0 (String host)
+ throws UnknownHostException
+ {
+ return getAllByName0(host, true);
+ }
+
+ /**
+ * package private so SocketPermission can call it
+ *
+ static InetAddress[] getAllByName0 (String host, boolean check)
+ throws UnknownHostException {
+ return getAllByName0 (host, null, check);
+ }
+
+ private static InetAddress[] getAllByName0 (String host, InetAddress reqAddr, boolean check)
+ throws UnknownHostException {
+
+ /* If it gets here it is presumed to be a hostname */
+ /* Cache.get can return: null, unknownAddress, or InetAddress[] */
+
+ /* make sure the connection to the host is allowed, before we
+ * give out a hostname
+ *
+ if (check) {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkConnect(host, -1);
+ }
+ }
+
+ InetAddress[] addresses = getCachedAddresses(host);
+
+ /* If no entry in cache, then do the host lookup *
+ if (addresses == null) {
+ addresses = getAddressesFromNameService(host, reqAddr);
+ }
+
+ if (addresses == unknown_array)
+ throw new UnknownHostException(host);
+
+ return addresses.clone();
+ }
+
+ private static InetAddress[] getAddressesFromNameService(String host, InetAddress reqAddr)
+ throws UnknownHostException
+ {
+ InetAddress[] addresses = null;
+ boolean success = false;
+ UnknownHostException ex = null;
+
+ // Check whether the host is in the lookupTable.
+ // 1) If the host isn't in the lookupTable when
+ // checkLookupTable() is called, checkLookupTable()
+ // would add the host in the lookupTable and
+ // return null. So we will do the lookup.
+ // 2) If the host is in the lookupTable when
+ // checkLookupTable() is called, the current thread
+ // would be blocked until the host is removed
+ // from the lookupTable. Then this thread
+ // should try to look up the addressCache.
+ // i) if it found the addresses in the
+ // addressCache, checkLookupTable() would
+ // return the addresses.
+ // ii) if it didn't find the addresses in the
+ // addressCache for any reason,
+ // it should add the host in the
+ // lookupTable and return null so the
+ // following code would do a lookup itself.
+ if ((addresses = checkLookupTable(host)) == null) {
+ try {
+ // This is the first thread which looks up the addresses
+ // this host or the cache entry for this host has been
+ // expired so this thread should do the lookup.
+ for (NameService nameService : nameServices) {
+ try {
+ /*
+ * Do not put the call to lookup() inside the
+ * constructor. if you do you will still be
+ * allocating space when the lookup fails.
+ *
+
+ addresses = nameService.lookupAllHostAddr(host);
+ success = true;
+ break;
+ } catch (UnknownHostException uhe) {
+ if (host.equalsIgnoreCase("localhost")) {
+ InetAddress[] local = new InetAddress[] { impl.loopbackAddress() };
+ addresses = local;
+ success = true;
+ break;
+ }
+ else {
+ addresses = unknown_array;
+ success = false;
+ ex = uhe;
+ }
+ }
+ }
+
+ // More to do?
+ if (reqAddr != null && addresses.length > 1 && !addresses[0].equals(reqAddr)) {
+ // Find it?
+ int i = 1;
+ for (; i < addresses.length; i++) {
+ if (addresses[i].equals(reqAddr)) {
+ break;
+ }
+ }
+ // Rotate
+ if (i < addresses.length) {
+ InetAddress tmp, tmp2 = reqAddr;
+ for (int j = 0; j < i; j++) {
+ tmp = addresses[j];
+ addresses[j] = tmp2;
+ tmp2 = tmp;
+ }
+ addresses[i] = tmp2;
+ }
+ }
+ // Cache the address.
+ cacheAddresses(host, addresses, success);
+
+ if (!success && ex != null)
+ throw ex;
+
+ } finally {
+ // Delete host from the lookupTable and notify
+ // all threads waiting on the lookupTable monitor.
+ updateLookupTable(host);
+ }
+ }
+
+ return addresses;
+ }
+
+
+ private static InetAddress[] checkLookupTable(String host) {
+ synchronized (lookupTable) {
+ // If the host isn't in the lookupTable, add it in the
+ // lookuptable and return null. The caller should do
+ // the lookup.
+ if (lookupTable.containsKey(host) == false) {
+ lookupTable.put(host, null);
+ return null;
+ }
+
+ // If the host is in the lookupTable, it means that another
+ // thread is trying to look up the addresses of this host.
+ // This thread should wait.
+ while (lookupTable.containsKey(host)) {
+ try {
+ lookupTable.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ // The other thread has finished looking up the addresses of
+ // the host. This thread should retry to get the addresses
+ // from the addressCache. If it doesn't get the addresses from
+ // the cache, it will try to look up the addresses itself.
+ InetAddress[] addresses = getCachedAddresses(host);
+ if (addresses == null) {
+ synchronized (lookupTable) {
+ lookupTable.put(host, null);
+ return null;
+ }
+ }
+
+ return addresses;
+ }
+
+ private static void updateLookupTable(String host) {
+ synchronized (lookupTable) {
+ lookupTable.remove(host);
+ lookupTable.notifyAll();
+ }
+ }
+ */
+ // END Android-removed: Resolves a hostname using Libcore.os.
+
+ /**
+ * Returns an {@code InetAddress} object given the raw IP address .
+ * The argument is in network byte order: the highest order
+ * byte of the address is in {@code getAddress()[0]}.
+ *
+ * <p> This method doesn't block, i.e. no reverse name service lookup
+ * is performed.
+ *
+ * <p> IPv4 address byte array must be 4 bytes long and IPv6 byte array
+ * must be 16 bytes long
+ *
+ * @param addr the raw IP address in network byte order
+ * @return an InetAddress object created from the raw IP address.
+ * @exception UnknownHostException if IP address is of illegal length
+ * @since 1.4
+ */
+ public static InetAddress getByAddress(byte[] addr)
+ throws UnknownHostException {
+ return getByAddress(null, addr);
+ }
+
+ // BEGIN Android-removed: Resolves a hostname using Libcore.os.
+ /*
+ private static InetAddress cachedLocalHost = null;
+ private static long cacheTime = 0;
+ private static final long maxCacheTime = 5000L;
+ private static final Object cacheLock = new Object();
+ */
+ // END Android-removed: Resolves a hostname using Libcore.os.
+
+ /**
+ * Returns the address of the local host. This is achieved by retrieving
+ * the name of the host from the system, then resolving that name into
+ * an {@code InetAddress}.
+ *
+ * <P>Note: The resolved address may be cached for a short period of time.
+ * </P>
+ *
+ * <p>If there is a security manager, its
+ * {@code checkConnect} method is called
+ * with the local host name and {@code -1}
+ * as its arguments to see if the operation is allowed.
+ * If the operation is not allowed, an InetAddress representing
+ * the loopback address is returned.
+ *
+ * @return the address of the local host.
+ *
+ * @exception UnknownHostException if the local host name could not
+ * be resolved into an address.
+ *
+ * @see SecurityManager#checkConnect
+ * @see java.net.InetAddress#getByName(java.lang.String)
+ */
+ public static InetAddress getLocalHost() throws UnknownHostException {
+ // BEGIN Android-changed: Resolves a hostname using Libcore.os.
+ /*
+ SecurityManager security = System.getSecurityManager();
+ try {
+ String local = impl.getLocalHostName();
+
+ if (security != null) {
+ security.checkConnect(local, -1);
+ }
+
+ if (local.equals("localhost")) {
+ return impl.loopbackAddress();
+ }
+
+ InetAddress ret = null;
+ synchronized (cacheLock) {
+ long now = System.currentTimeMillis();
+ if (cachedLocalHost != null) {
+ if ((now - cacheTime) < maxCacheTime) // Less than 5s old?
+ ret = cachedLocalHost;
+ else
+ cachedLocalHost = null;
+ }
+
+ // we are calling getAddressesFromNameService directly
+ // to avoid getting localHost from cache
+ if (ret == null) {
+ InetAddress[] localAddrs;
+ try {
+ localAddrs =
+ InetAddress.getAddressesFromNameService(local, null);
+ } catch (UnknownHostException uhe) {
+ // Rethrow with a more informative error message.
+ UnknownHostException uhe2 =
+ new UnknownHostException(local + ": " +
+ uhe.getMessage());
+ uhe2.initCause(uhe);
+ throw uhe2;
+ }
+ cachedLocalHost = localAddrs[0];
+ cacheTime = now;
+ ret = localAddrs[0];
+ }
+ }
+ return ret;
+ } catch (java.lang.SecurityException e) {
+ return impl.loopbackAddress();
+ }
+ */
+ String local = Libcore.os.uname().nodename;
+ return impl.lookupAllHostAddr(local, NETID_UNSET)[0];
+ // END Android-changed: Resolves a hostname using Libcore.os.
+ }
+
+ // BEGIN Android-removed: Android doesn't need to call native init.
+ /**
+ * Perform class load-time initializations.
+ *
+ private static native void init();
+ */
+ // END Android-removed: Android doesn't need to call native init.
+
+ /*
+ * Returns the InetAddress representing anyLocalAddress
+ * (typically 0.0.0.0 or ::0)
+ */
+ static InetAddress anyLocalAddress() {
+ return impl.anyLocalAddress();
+ }
+
+ // BEGIN Android-removed: Android doesn't load user-provided implementation.
+ /*
+ * Load and instantiate an underlying impl class
+ *
+ static InetAddressImpl loadImpl(String implName) {
+ Object impl = null;
+
+ /*
+ * Property "impl.prefix" will be prepended to the classname
+ * of the implementation object we instantiate, to which we
+ * delegate the real work (like native methods). This
+ * property can vary across implementations of the java.
+ * classes. The default is an empty String "".
+ *
+ String prefix = AccessController.doPrivileged(
+ new GetPropertyAction("impl.prefix", ""));
+ try {
+ impl = Class.forName("java.net." + prefix + implName).newInstance();
+ } catch (ClassNotFoundException e) {
+ System.err.println("Class not found: java.net." + prefix +
+ implName + ":\ncheck impl.prefix property " +
+ "in your properties file.");
+ } catch (InstantiationException e) {
+ System.err.println("Could not instantiate: java.net." + prefix +
+ implName + ":\ncheck impl.prefix property " +
+ "in your properties file.");
+ } catch (IllegalAccessException e) {
+ System.err.println("Cannot access class: java.net." + prefix +
+ implName + ":\ncheck impl.prefix property " +
+ "in your properties file.");
+ }
+
+ if (impl == null) {
+ try {
+ impl = Class.forName(implName).newInstance();
+ } catch (Exception e) {
+ throw new Error("System property impl.prefix incorrect");
+ }
+ }
+
+ return (InetAddressImpl) impl;
+ }
+ */
+ // END Android-removed: Android doesn't load user-provided implementation.
+
+ private void readObjectNoData (ObjectInputStream s) throws
+ IOException, ClassNotFoundException {
+ // Android-changed: Don't use null to mean the boot classloader.
+ if (getClass().getClassLoader() != BOOT_CLASSLOADER) {
+ throw new SecurityException ("invalid address type");
+ }
+ }
+
+ // Android-changed: Don't use null to mean the boot classloader.
+ private static final ClassLoader BOOT_CLASSLOADER = Object.class.getClassLoader();
+
+ private void readObject (ObjectInputStream s) throws
+ IOException, ClassNotFoundException {
+ // Android-changed: Don't use null to mean the boot classloader.
+ if (getClass().getClassLoader() != BOOT_CLASSLOADER) {
+ throw new SecurityException ("invalid address type");
+ }
+ GetField gf = s.readFields();
+ String host = (String)gf.get("hostName", null);
+ int address= gf.get("address", 0);
+ int family= gf.get("family", 0);
+ holder = new InetAddressHolder(host, address, family);
+ }
+
+ /* needed because the serializable fields no longer exist */
+
+ /**
+ * @serialField hostName String
+ * @serialField address int
+ * @serialField family int
+ */
+ private static final ObjectStreamField[] serialPersistentFields = {
+ new ObjectStreamField("hostName", String.class),
+ new ObjectStreamField("address", int.class),
+ new ObjectStreamField("family", int.class),
+ };
+
+ private void writeObject (ObjectOutputStream s) throws
+ IOException {
+ // Android-changed: Don't use null to mean the boot classloader.
+ if (getClass().getClassLoader() != BOOT_CLASSLOADER) {
+ throw new SecurityException ("invalid address type");
+ }
+ PutField pf = s.putFields();
+ pf.put("hostName", holder().hostName);
+ pf.put("address", holder().address);
+ pf.put("family", holder().family);
+ s.writeFields();
+ s.flush();
+ }
+
+ static final int NETID_UNSET = 0;
+
+ // BEGIN Android-added: Add methods required by frameworks/base.
+ // Particularly those required to deal with scope ids.
+ /**
+ * Returns true if the string is a valid numeric IPv4 or IPv6 address (such as "192.168.0.1").
+ *
+ * <p>This copes with all forms of address that Java supports, detailed in the
+ * {@link InetAddress} class documentation. An empty string is not treated as numeric.
+ *
+ * @hide used by frameworks/base to ensure that a getAllByName won't cause a DNS lookup.
+ * @deprecated Use {@link InetAddressUtils#isNumericAddress(String)} instead, if possible.
+ * @throws NullPointerException if the {@code address} is {@code null}.
+ */
+ @Deprecated
+ public static boolean isNumeric(String address) {
+ return InetAddressUtils.parseNumericAddressNoThrowStripOptionalBrackets(address) != null;
+ }
+
+ /**
+ * Returns an InetAddress corresponding to the given numeric address (such
+ * as {@code "192.168.0.1"} or {@code "2001:4860:800d::68"}).
+ *
+ * <p>This method will never do a DNS lookup. Non-numeric addresses are errors. Passing either
+ * an empty string or a {@code null} as the {@code numericAddress} will return the
+ * {@link Inet6Address#LOOPBACK} address.
+ *
+ * @hide used by frameworks/base's NetworkUtils.numericToInetAddress
+ * @throws IllegalArgumentException if {@code numericAddress} is not a numeric address
+ * @deprecated Use {@link InetAddressUtils#parseNumericAddress(String)} instead, if possible.
+ */
+ @Deprecated
+ public static InetAddress parseNumericAddress(String numericAddress) {
+ if (numericAddress == null || numericAddress.isEmpty()) {
+ return Inet6Address.LOOPBACK;
+ }
+ InetAddress result = InetAddressUtils
+ .parseNumericAddressNoThrowStripOptionalBrackets(numericAddress);
+ if (result == null) {
+ throw new IllegalArgumentException("Not a numeric address: " + numericAddress);
+ }
+ return result;
+ }
+
+ /**
+ * Removes all entries from the VM's DNS cache. This does not affect the C library's DNS
+ * cache, nor any caching DNS servers between you and the canonical server.
+ * @hide
+ */
+ public static void clearDnsCache() {
+ impl.clearAddressCache();
+ }
+ // END Android-added: Add methods required by frameworks/base.
+ // BEGIN Android-added: Support for network (netId)-specific DNS resolution.
+ /**
+ * Operates identically to {@code getByName} except host resolution is
+ * performed on the network designated by {@code netId}.
+ *
+ * @param host
+ * the hostName to be resolved to an address or {@code null}.
+ * @param netId the network to use for host resolution.
+ * @return the {@code InetAddress} instance representing the host.
+ * @throws UnknownHostException if the address lookup fails.
+ * @hide internal use only
+ */
+ public static InetAddress getByNameOnNet(String host, int netId) throws UnknownHostException {
+ return impl.lookupAllHostAddr(host, netId)[0];
+ }
+
+ /**
+ * Operates identically to {@code getAllByName} except host resolution is
+ * performed on the network designated by {@code netId}.
+ *
+ * @param host the hostname or literal IP string to be resolved.
+ * @param netId the network to use for host resolution.
+ * @return the array of addresses associated with the specified host.
+ * @throws UnknownHostException if the address lookup fails.
+ * @hide internal use only
+ */
+ public static InetAddress[] getAllByNameOnNet(String host, int netId) throws UnknownHostException {
+ return impl.lookupAllHostAddr(host, netId).clone();
+ }
+ // END Android-added: Support for network (netId)-specific DNS resolution.
+}
+// BEGIN Android-removed: Android doesn't load user-provided implementation.
+/*
+ * Simple factory to create the impl
+ *
+class InetAddressImplFactory {
+
+ static InetAddressImpl create() {
+ return InetAddress.loadImpl(isIPv6Supported() ?
+ "Inet6AddressImpl" : "Inet4AddressImpl");
+ }
+
+ static native boolean isIPv6Supported();
+}
+*/
+// END Android-removed: Android doesn't load user-provided implementation.
diff --git a/java/net/InetAddressContainer.java b/java/net/InetAddressContainer.java
new file mode 100644
index 0000000..28b6402
--- /dev/null
+++ b/java/net/InetAddressContainer.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+class InetAddressContainer {
+ InetAddress addr;
+}
diff --git a/java/net/InetAddressImpl.java b/java/net/InetAddressImpl.java
new file mode 100644
index 0000000..a636c1e
--- /dev/null
+++ b/java/net/InetAddressImpl.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+import java.io.IOException;
+/*
+ * Package private interface to "implementation" used by
+ * {@link InetAddress}.
+ * <p>
+ * See {@link java.net.Inet4AddressImp} and
+ * {@link java.net.Inet6AddressImp}.
+ *
+ * @since 1.4
+ */
+interface InetAddressImpl {
+
+ // BEGIN Android-changed: Rewrote hostname lookup methods on top of Libcore.os.
+ /*
+ String getLocalHostName() throws UnknownHostException;
+ InetAddress[]
+ lookupAllHostAddr(String hostname) throws UnknownHostException;
+ */
+ /**
+ * Lookup all addresses for {@code hostname} on the given {@code netId}.
+ */
+ InetAddress[] lookupAllHostAddr(String hostname, int netId) throws UnknownHostException;
+
+ /**
+ * Reverse-lookup the host name for a given {@code addr}.
+ */
+ String getHostByAddr(byte[] addr) throws UnknownHostException;
+
+ /**
+ * Clear address caches (if any).
+ */
+ public void clearAddressCache();
+ // END Android-changed: Rewrote hostname lookup methods on top of Libcore.os.
+
+ /**
+ * Return the "any" local address.
+ */
+ InetAddress anyLocalAddress();
+
+ // Android-changed: Let loopbackAddresses() return both Inet4 and Inet6 loopbacks.
+ // InetAddress loopbackAddress();
+ /**
+ * Return a list of loop back adresses for this implementation.
+ */
+ InetAddress[] loopbackAddresses();
+
+ /**
+ * Whether {@code addr} is reachable over {@code netif}.
+ */
+ boolean isReachable(InetAddress addr, int timeout, NetworkInterface netif,
+ int ttl) throws IOException;
+}
diff --git a/java/net/InetSocketAddress.annotated.java b/java/net/InetSocketAddress.annotated.java
new file mode 100644
index 0000000..554b8e3
--- /dev/null
+++ b/java/net/InetSocketAddress.annotated.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+
+@SuppressWarnings({"unchecked", "deprecation", "all"})
+public class InetSocketAddress extends java.net.SocketAddress {
+
[email protected]
+public InetSocketAddress() { throw new RuntimeException("Stub!"); }
+
+public InetSocketAddress(int port) { throw new RuntimeException("Stub!"); }
+
+public InetSocketAddress(java.net.InetAddress addr, int port) { throw new RuntimeException("Stub!"); }
+
+public InetSocketAddress(java.lang.String hostname, int port) { throw new RuntimeException("Stub!"); }
+
+public static java.net.InetSocketAddress createUnresolved(java.lang.String host, int port) { throw new RuntimeException("Stub!"); }
+
+public final int getPort() { throw new RuntimeException("Stub!"); }
+
+public final java.net.InetAddress getAddress() { throw new RuntimeException("Stub!"); }
+
+public final java.lang.String getHostName() { throw new RuntimeException("Stub!"); }
+
+public final java.lang.String getHostString() { throw new RuntimeException("Stub!"); }
+
+public final boolean isUnresolved() { throw new RuntimeException("Stub!"); }
+
+public java.lang.String toString() { throw new RuntimeException("Stub!"); }
+
+public final boolean equals(java.lang.Object obj) { throw new RuntimeException("Stub!"); }
+
+public final int hashCode() { throw new RuntimeException("Stub!"); }
+}
+
diff --git a/java/net/InetSocketAddress.java b/java/net/InetSocketAddress.java
new file mode 100644
index 0000000..74b559b
--- /dev/null
+++ b/java/net/InetSocketAddress.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.net;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamException;
+import java.io.ObjectStreamField;
+
+/**
+ *
+ * This class implements an IP Socket Address (IP address + port number)
+ * It can also be a pair (hostname + port number), in which case an attempt
+ * will be made to resolve the hostname. If resolution fails then the address
+ * is said to be <I>unresolved</I> but can still be used on some circumstances
+ * like connecting through a proxy.
+ * <p>
+ * It provides an immutable object used by sockets for binding, connecting, or
+ * as returned values.
+ * <p>
+ * The <i>wildcard</i> is a special local IP address. It usually means "any"
+ * and can only be used for {@code bind} operations.
+ *
+ * @see java.net.Socket
+ * @see java.net.ServerSocket
+ * @since 1.4
+ */
+public class InetSocketAddress
+ extends SocketAddress
+{
+ // Private implementation class pointed to by all public methods.
+ private static class InetSocketAddressHolder {
+ // The hostname of the Socket Address
+ private String hostname;
+ // The IP address of the Socket Address
+ private InetAddress addr;
+ // The port number of the Socket Address
+ private int port;
+
+ private InetSocketAddressHolder(String hostname, InetAddress addr, int port) {
+ this.hostname = hostname;
+ this.addr = addr;
+ this.port = port;
+ }
+
+ private int getPort() {
+ return port;
+ }
+
+ private InetAddress getAddress() {
+ return addr;
+ }
+
+ private String getHostName() {
+ if (hostname != null)
+ return hostname;
+ if (addr != null)
+ return addr.getHostName();
+ return null;
+ }
+
+ private String getHostString() {
+ if (hostname != null)
+ return hostname;
+ if (addr != null) {
+ if (addr.holder().getHostName() != null)
+ return addr.holder().getHostName();
+ else
+ return addr.getHostAddress();
+ }
+ return null;
+ }
+
+ private boolean isUnresolved() {
+ return addr == null;
+ }
+
+ @Override
+ public String toString() {
+ if (isUnresolved()) {
+ return hostname + ":" + port;
+ } else {
+ return addr.toString() + ":" + port;
+ }
+ }
+
+ @Override
+ public final boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof InetSocketAddressHolder))
+ return false;
+ InetSocketAddressHolder that = (InetSocketAddressHolder)obj;
+ boolean sameIP;
+ if (addr != null)
+ sameIP = addr.equals(that.addr);
+ else if (hostname != null)
+ sameIP = (that.addr == null) &&
+ hostname.equalsIgnoreCase(that.hostname);
+ else
+ sameIP = (that.addr == null) && (that.hostname == null);
+ return sameIP && (port == that.port);
+ }
+
+ @Override
+ public final int hashCode() {
+ if (addr != null)
+ return addr.hashCode() + port;
+ if (hostname != null)
+ return hostname.toLowerCase().hashCode() + port;
+ return port;
+ }
+ }
+
+ private final transient InetSocketAddressHolder holder;
+
+ private static final long serialVersionUID = 5076001401234631237L;
+
+ private static int checkPort(int port) {
+ if (port < 0 || port > 0xFFFF)
+ throw new IllegalArgumentException("port out of range:" + port);
+ return port;
+ }
+
+ private static String checkHost(String hostname) {
+ if (hostname == null)
+ throw new IllegalArgumentException("hostname can't be null");
+ return hostname;
+ }
+
+ // BEGIN Android-added: InetSocketAddress() ctor used by IoBridge.
+ /**
+ * @hide internal use only
+ */
+ public InetSocketAddress() {
+ // These will be filled in the native implementation of recvfrom.
+ holder = new InetSocketAddressHolder(null, null, 0);
+ }
+ // END Android-added: InetSocketAddress() ctor used by IoBridge.
+
+ /**
+ * Creates a socket address where the IP address is the wildcard address
+ * and the port number a specified value.
+ * <p>
+ * A valid port value is between 0 and 65535.
+ * A port number of {@code zero} will let the system pick up an
+ * ephemeral port in a {@code bind} operation.
+ * <p>
+ * @param port The port number
+ * @throws IllegalArgumentException if the port parameter is outside the specified
+ * range of valid port values.
+ */
+ public InetSocketAddress(int port) {
+ // Android-changed: Defaults to IPv6.
+ // this(InetAddress.anyLocalAddress(), port);
+ this((InetAddress)null, port);
+ }
+
+ /**
+ *
+ * Creates a socket address from an IP address and a port number.
+ * <p>
+ * A valid port value is between 0 and 65535.
+ * A port number of {@code zero} will let the system pick up an
+ * ephemeral port in a {@code bind} operation.
+ * <P>
+ * A {@code null} address will assign the <i>wildcard</i> address.
+ * <p>
+ * @param addr The IP address
+ * @param port The port number
+ * @throws IllegalArgumentException if the port parameter is outside the specified
+ * range of valid port values.
+ */
+ public InetSocketAddress(InetAddress addr, int port) {
+ holder = new InetSocketAddressHolder(
+ null,
+ // Android-changed: Defaults to IPv6 if addr is null.
+ // addr == null ? InetAddress.anyLocalAddress() : addr,
+ addr == null ? Inet6Address.ANY : addr,
+ checkPort(port));
+ }
+
+ /**
+ *
+ * Creates a socket address from a hostname and a port number.
+ * <p>
+ * An attempt will be made to resolve the hostname into an InetAddress.
+ * If that attempt fails, the address will be flagged as <I>unresolved</I>.
+ * <p>
+ * If there is a security manager, its {@code checkConnect} method
+ * is called with the host name as its argument to check the permission
+ * to resolve it. This could result in a SecurityException.
+ * <P>
+ * A valid port value is between 0 and 65535.
+ * A port number of {@code zero} will let the system pick up an
+ * ephemeral port in a {@code bind} operation.
+ * <P>
+ * @param hostname the Host name
+ * @param port The port number
+ * @throws IllegalArgumentException if the port parameter is outside the range
+ * of valid port values, or if the hostname parameter is <TT>null</TT>.
+ * @throws SecurityException if a security manager is present and
+ * permission to resolve the host name is
+ * denied.
+ * @see #isUnresolved()
+ */
+ public InetSocketAddress(String hostname, int port) {
+ checkHost(hostname);
+ InetAddress addr = null;
+ String host = null;
+ try {
+ addr = InetAddress.getByName(hostname);
+ } catch(UnknownHostException e) {
+ host = hostname;
+ }
+ holder = new InetSocketAddressHolder(host, addr, checkPort(port));
+ }
+
+ // private constructor for creating unresolved instances
+ private InetSocketAddress(int port, String hostname) {
+ holder = new InetSocketAddressHolder(hostname, null, port);
+ }
+
+ /**
+ *
+ * Creates an unresolved socket address from a hostname and a port number.
+ * <p>
+ * No attempt will be made to resolve the hostname into an InetAddress.
+ * The address will be flagged as <I>unresolved</I>.
+ * <p>
+ * A valid port value is between 0 and 65535.
+ * A port number of {@code zero} will let the system pick up an
+ * ephemeral port in a {@code bind} operation.
+ * <P>
+ * @param host the Host name
+ * @param port The port number
+ * @throws IllegalArgumentException if the port parameter is outside
+ * the range of valid port values, or if the hostname
+ * parameter is <TT>null</TT>.
+ * @see #isUnresolved()
+ * @return a {@code InetSocketAddress} representing the unresolved
+ * socket address
+ * @since 1.5
+ */
+ public static InetSocketAddress createUnresolved(String host, int port) {
+ return new InetSocketAddress(checkPort(port), checkHost(host));
+ }
+
+ /**
+ * @serialField hostname String
+ * @serialField addr InetAddress
+ * @serialField port int
+ */
+ private static final ObjectStreamField[] serialPersistentFields = {
+ new ObjectStreamField("hostname", String.class),
+ new ObjectStreamField("addr", InetAddress.class),
+ new ObjectStreamField("port", int.class)};
+
+ private void writeObject(ObjectOutputStream out)
+ throws IOException
+ {
+ // Don't call defaultWriteObject()
+ ObjectOutputStream.PutField pfields = out.putFields();
+ pfields.put("hostname", holder.hostname);
+ pfields.put("addr", holder.addr);
+ pfields.put("port", holder.port);
+ out.writeFields();
+ }
+
+ private void readObject(ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ // Don't call defaultReadObject()
+ ObjectInputStream.GetField oisFields = in.readFields();
+ final String oisHostname = (String)oisFields.get("hostname", null);
+ final InetAddress oisAddr = (InetAddress)oisFields.get("addr", null);
+ final int oisPort = oisFields.get("port", -1);
+
+ // Check that our invariants are satisfied
+ checkPort(oisPort);
+ if (oisHostname == null && oisAddr == null)
+ throw new InvalidObjectException("hostname and addr " +
+ "can't both be null");
+
+ InetSocketAddressHolder h = new InetSocketAddressHolder(oisHostname,
+ oisAddr,
+ oisPort);
+ UNSAFE.putObject(this, FIELDS_OFFSET, h);
+ }
+
+ private void readObjectNoData()
+ throws ObjectStreamException
+ {
+ throw new InvalidObjectException("Stream data required");
+ }
+
+ private static final long FIELDS_OFFSET;
+ private static final sun.misc.Unsafe UNSAFE;
+ static {
+ try {
+ sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
+ FIELDS_OFFSET = unsafe.objectFieldOffset(
+ InetSocketAddress.class.getDeclaredField("holder"));
+ UNSAFE = unsafe;
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
+
+ /**
+ * Gets the port number.
+ *
+ * @return the port number.
+ */
+ public final int getPort() {
+ return holder.getPort();
+ }
+
+ /**
+ *
+ * Gets the {@code InetAddress}.
+ *
+ * @return the InetAdress or {@code null} if it is unresolved.
+ */
+ public final InetAddress getAddress() {
+ return holder.getAddress();
+ }
+
+ /**
+ * Gets the {@code hostname}.
+ * Note: This method may trigger a name service reverse lookup if the
+ * address was created with a literal IP address.
+ *
+ * @return the hostname part of the address.
+ */
+ public final String getHostName() {
+ return holder.getHostName();
+ }
+
+ /**
+ * Returns the hostname, or the String form of the address if it
+ * doesn't have a hostname (it was created using a literal).
+ * This has the benefit of <b>not</b> attempting a reverse lookup.
+ *
+ * @return the hostname, or String representation of the address.
+ * @since 1.7
+ */
+ public final String getHostString() {
+ return holder.getHostString();
+ }
+
+ /**
+ * Checks whether the address has been resolved or not.
+ *
+ * @return {@code true} if the hostname couldn't be resolved into
+ * an {@code InetAddress}.
+ */
+ public final boolean isUnresolved() {
+ return holder.isUnresolved();
+ }
+
+ /**
+ * Constructs a string representation of this InetSocketAddress.
+ * This String is constructed by calling toString() on the InetAddress
+ * and concatenating the port number (with a colon). If the address
+ * is unresolved then the part before the colon will only contain the hostname.
+ *
+ * @return a string representation of this object.
+ */
+ @Override
+ public String toString() {
+ return holder.toString();
+ }
+
+ /**
+ * Compares this object against the specified object.
+ * The result is {@code true} if and only if the argument is
+ * not {@code null} and it represents the same address as
+ * this object.
+ * <p>
+ * Two instances of {@code InetSocketAddress} represent the same
+ * address if both the InetAddresses (or hostnames if it is unresolved) and port
+ * numbers are equal.
+ * If both addresses are unresolved, then the hostname and the port number
+ * are compared.
+ *
+ * Note: Hostnames are case insensitive. e.g. "FooBar" and "foobar" are
+ * considered equal.
+ *
+ * @param obj the object to compare against.
+ * @return {@code true} if the objects are the same;
+ * {@code false} otherwise.
+ * @see java.net.InetAddress#equals(java.lang.Object)
+ */
+ @Override
+ public final boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof InetSocketAddress))
+ return false;
+ return holder.equals(((InetSocketAddress) obj).holder);
+ }
+
+ /**
+ * Returns a hashcode for this socket address.
+ *
+ * @return a hash code value for this socket address.
+ */
+ @Override
+ public final int hashCode() {
+ return holder.hashCode();
+ }
+}
diff --git a/java/net/InterfaceAddress.java b/java/net/InterfaceAddress.java
new file mode 100644
index 0000000..85f9e58
--- /dev/null
+++ b/java/net/InterfaceAddress.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * This class represents a Network Interface address. In short it's an
+ * IP address, a subnet mask and a broadcast address when the address is
+ * an IPv4 one. An IP address and a network prefix length in the case
+ * of IPv6 address.
+ *
+ * @see java.net.NetworkInterface
+ * @since 1.6
+ */
+public class InterfaceAddress {
+ private InetAddress address = null;
+ private Inet4Address broadcast = null;
+ private short maskLength = 0;
+
+ /*
+ * Package private constructor. Can't be built directly, instances are
+ * obtained through the NetworkInterface class.
+ */
+ InterfaceAddress() {
+ }
+
+ // BEGIN Android-added: Rewrote NetworkInterface on top of Libcore.io.
+ InterfaceAddress(InetAddress address, Inet4Address broadcast, InetAddress netmask) {
+ this.address = address;
+ this.broadcast = broadcast;
+ this.maskLength = countPrefixLength(netmask);
+ }
+
+ /**
+ * Counts the prefix length for the netmask address.
+ *
+ * A valid netmask address must start with a continuous sequence of 1, followed by a continuous
+ * sequence of 0.
+ */
+ private short countPrefixLength(InetAddress netmask) {
+ short count = 0;
+ for (byte b : netmask.getAddress()) {
+ for (; b != 0; b <<= 1, ++count);
+ }
+ return count;
+ }
+ // END Android-added: Rewrote NetworkInterface on top of Libcore.io.
+
+ /**
+ * Returns an {@code InetAddress} for this address.
+ *
+ * @return the {@code InetAddress} for this address.
+ */
+ public InetAddress getAddress() {
+ return address;
+ }
+
+ /**
+ * Returns an {@code InetAddress} for the broadcast address
+ * for this InterfaceAddress.
+ * <p>
+ * Only IPv4 networks have broadcast address therefore, in the case
+ * of an IPv6 network, {@code null} will be returned.
+ *
+ * @return the {@code InetAddress} representing the broadcast
+ * address or {@code null} if there is no broadcast address.
+ */
+ public InetAddress getBroadcast() {
+ return broadcast;
+ }
+
+ /**
+ * Returns the network prefix length for this address. This is also known
+ * as the subnet mask in the context of IPv4 addresses.
+ * Typical IPv4 values would be 8 (255.0.0.0), 16 (255.255.0.0)
+ * or 24 (255.255.255.0). <p>
+ * Typical IPv6 values would be 128 (::1/128) or 10 (fe80::203:baff:fe27:1243/10)
+ *
+ * @return a {@code short} representing the prefix length for the
+ * subnet of that address.
+ */
+ public short getNetworkPrefixLength() {
+ return maskLength;
+ }
+
+ /**
+ * Compares this object against the specified object.
+ * The result is {@code true} if and only if the argument is
+ * not {@code null} and it represents the same interface address as
+ * this object.
+ * <p>
+ * Two instances of {@code InterfaceAddress} represent the same
+ * address if the InetAddress, the prefix length and the broadcast are
+ * the same for both.
+ *
+ * @param obj the object to compare against.
+ * @return {@code true} if the objects are the same;
+ * {@code false} otherwise.
+ * @see java.net.InterfaceAddress#hashCode()
+ */
+ public boolean equals(Object obj) {
+ if (!(obj instanceof InterfaceAddress)) {
+ return false;
+ }
+ InterfaceAddress cmp = (InterfaceAddress) obj;
+ if ( !(address == null ? cmp.address == null : address.equals(cmp.address)) )
+ return false;
+ if ( !(broadcast == null ? cmp.broadcast == null : broadcast.equals(cmp.broadcast)) )
+ return false;
+ if (maskLength != cmp.maskLength)
+ return false;
+ return true;
+ }
+
+ /**
+ * Returns a hashcode for this Interface address.
+ *
+ * @return a hash code value for this Interface address.
+ */
+ public int hashCode() {
+ return address.hashCode() + ((broadcast != null) ? broadcast.hashCode() : 0) + maskLength;
+ }
+
+ /**
+ * Converts this Interface address to a {@code String}. The
+ * string returned is of the form: InetAddress / prefix length [ broadcast address ].
+ *
+ * @return a string representation of this Interface address.
+ */
+ public String toString() {
+ return address + "/" + maskLength + " [" + broadcast + "]";
+ }
+
+}
diff --git a/java/net/JarURLConnection.java b/java/net/JarURLConnection.java
new file mode 100644
index 0000000..70dedec
--- /dev/null
+++ b/java/net/JarURLConnection.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+import java.util.jar.JarFile;
+import java.util.jar.JarEntry;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.security.Permission;
+import sun.net.www.ParseUtil;
+
+/**
+ * A URL Connection to a Java ARchive (JAR) file or an entry in a JAR
+ * file.
+ *
+ * <p>The syntax of a JAR URL is:
+ *
+ * <pre>
+ * jar:<url>!/{entry}
+ * </pre>
+ *
+ * <p>for example:
+ *
+ * <p>{@code jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class}
+ *
+ * <p>Jar URLs should be used to refer to a JAR file or entries in
+ * a JAR file. The example above is a JAR URL which refers to a JAR
+ * entry. If the entry name is omitted, the URL refers to the whole
+ * JAR file:
+ *
+ * {@code jar:http://www.foo.com/bar/baz.jar!/}
+ *
+ * <p>Users should cast the generic URLConnection to a
+ * JarURLConnection when they know that the URL they created is a JAR
+ * URL, and they need JAR-specific functionality. For example:
+ *
+ * <pre>
+ * URL url = new URL("jar:file:/home/duke/duke.jar!/");
+ * JarURLConnection jarConnection = (JarURLConnection)url.openConnection();
+ * Manifest manifest = jarConnection.getManifest();
+ * </pre>
+ *
+ * <p>JarURLConnection instances can only be used to read from JAR files.
+ * It is not possible to get a {@link java.io.OutputStream} to modify or write
+ * to the underlying JAR file using this class.
+ * <p>Examples:
+ *
+ * <dl>
+ *
+ * <dt>A Jar entry
+ * <dd>{@code jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class}
+ *
+ * <dt>A Jar file
+ * <dd>{@code jar:http://www.foo.com/bar/baz.jar!/}
+ *
+ * <dt>A Jar directory
+ * <dd>{@code jar:http://www.foo.com/bar/baz.jar!/COM/foo/}
+ *
+ * </dl>
+ *
+ * <p>{@code !/} is referred to as the <em>separator</em>.
+ *
+ * <p>When constructing a JAR url via {@code new URL(context, spec)},
+ * the following rules apply:
+ *
+ * <ul>
+ *
+ * <li>if there is no context URL and the specification passed to the
+ * URL constructor doesn't contain a separator, the URL is considered
+ * to refer to a JarFile.
+ *
+ * <li>if there is a context URL, the context URL is assumed to refer
+ * to a JAR file or a Jar directory.
+ *
+ * <li>if the specification begins with a '/', the Jar directory is
+ * ignored, and the spec is considered to be at the root of the Jar
+ * file.
+ *
+ * <p>Examples:
+ *
+ * <dl>
+ *
+ * <dt>context: <b>jar:http://www.foo.com/bar/jar.jar!/</b>,
+ * spec:<b>baz/entry.txt</b>
+ *
+ * <dd>url:<b>jar:http://www.foo.com/bar/jar.jar!/baz/entry.txt</b>
+ *
+ * <dt>context: <b>jar:http://www.foo.com/bar/jar.jar!/baz</b>,
+ * spec:<b>entry.txt</b>
+ *
+ * <dd>url:<b>jar:http://www.foo.com/bar/jar.jar!/baz/entry.txt</b>
+ *
+ * <dt>context: <b>jar:http://www.foo.com/bar/jar.jar!/baz</b>,
+ * spec:<b>/entry.txt</b>
+ *
+ * <dd>url:<b>jar:http://www.foo.com/bar/jar.jar!/entry.txt</b>
+ *
+ * </dl>
+ *
+ * </ul>
+ *
+ * @see java.net.URL
+ * @see java.net.URLConnection
+ *
+ * @see java.util.jar.JarFile
+ * @see java.util.jar.JarInputStream
+ * @see java.util.jar.Manifest
+ * @see java.util.zip.ZipEntry
+ *
+ * @author Benjamin Renaud
+ * @since 1.2
+ */
+public abstract class JarURLConnection extends URLConnection {
+
+ private URL jarFileURL;
+ private String entryName;
+
+ /**
+ * The connection to the JAR file URL, if the connection has been
+ * initiated. This should be set by connect.
+ */
+ protected URLConnection jarFileURLConnection;
+
+ /**
+ * Creates the new JarURLConnection to the specified URL.
+ * @param url the URL
+ * @throws MalformedURLException if no legal protocol
+ * could be found in a specification string or the
+ * string could not be parsed.
+ */
+
+ protected JarURLConnection(URL url) throws MalformedURLException {
+ super(url);
+ parseSpecs(url);
+ }
+
+ /* get the specs for a given url out of the cache, and compute and
+ * cache them if they're not there.
+ */
+ private void parseSpecs(URL url) throws MalformedURLException {
+ String spec = url.getFile();
+
+ int separator = spec.indexOf("!/");
+ /*
+ * REMIND: we don't handle nested JAR URLs
+ */
+ if (separator == -1) {
+ throw new MalformedURLException("no !/ found in url spec:" + spec);
+ }
+
+ jarFileURL = new URL(spec.substring(0, separator++));
+ entryName = null;
+
+ /* if ! is the last letter of the innerURL, entryName is null */
+ if (++separator != spec.length()) {
+ entryName = spec.substring(separator, spec.length());
+ entryName = ParseUtil.decode (entryName);
+ }
+ }
+
+ /**
+ * Returns the URL for the Jar file for this connection.
+ *
+ * @return the URL for the Jar file for this connection.
+ */
+ public URL getJarFileURL() {
+ return jarFileURL;
+ }
+
+ /**
+ * Return the entry name for this connection. This method
+ * returns null if the JAR file URL corresponding to this
+ * connection points to a JAR file and not a JAR file entry.
+ *
+ * @return the entry name for this connection, if any.
+ */
+ public String getEntryName() {
+ return entryName;
+ }
+
+ /**
+ * Return the JAR file for this connection.
+ *
+ * @return the JAR file for this connection. If the connection is
+ * a connection to an entry of a JAR file, the JAR file object is
+ * returned
+ *
+ * @exception IOException if an IOException occurs while trying to
+ * connect to the JAR file for this connection.
+ *
+ * @see #connect
+ */
+ public abstract JarFile getJarFile() throws IOException;
+
+ /**
+ * Returns the Manifest for this connection, or null if none.
+ *
+ * @return the manifest object corresponding to the JAR file object
+ * for this connection.
+ *
+ * @exception IOException if getting the JAR file for this
+ * connection causes an IOException to be thrown.
+ *
+ * @see #getJarFile
+ */
+ public Manifest getManifest() throws IOException {
+ return getJarFile().getManifest();
+ }
+
+ /**
+ * Return the JAR entry object for this connection, if any. This
+ * method returns null if the JAR file URL corresponding to this
+ * connection points to a JAR file and not a JAR file entry.
+ *
+ * @return the JAR entry object for this connection, or null if
+ * the JAR URL for this connection points to a JAR file.
+ *
+ * @exception IOException if getting the JAR file for this
+ * connection causes an IOException to be thrown.
+ *
+ * @see #getJarFile
+ * @see #getJarEntry
+ */
+ public JarEntry getJarEntry() throws IOException {
+ return getJarFile().getJarEntry(entryName);
+ }
+
+ /**
+ * Return the Attributes object for this connection if the URL
+ * for it points to a JAR file entry, null otherwise.
+ *
+ * @return the Attributes object for this connection if the URL
+ * for it points to a JAR file entry, null otherwise.
+ *
+ * @exception IOException if getting the JAR entry causes an
+ * IOException to be thrown.
+ *
+ * @see #getJarEntry
+ */
+ public Attributes getAttributes() throws IOException {
+ JarEntry e = getJarEntry();
+ return e != null ? e.getAttributes() : null;
+ }
+
+ /**
+ * Returns the main Attributes for the JAR file for this
+ * connection.
+ *
+ * @return the main Attributes for the JAR file for this
+ * connection.
+ *
+ * @exception IOException if getting the manifest causes an
+ * IOException to be thrown.
+ *
+ * @see #getJarFile
+ * @see #getManifest
+ */
+ public Attributes getMainAttributes() throws IOException {
+ Manifest man = getManifest();
+ return man != null ? man.getMainAttributes() : null;
+ }
+
+ /**
+ * Return the Certificate object for this connection if the URL
+ * for it points to a JAR file entry, null otherwise. This method
+ * can only be called once
+ * the connection has been completely verified by reading
+ * from the input stream until the end of the stream has been
+ * reached. Otherwise, this method will return {@code null}
+ *
+ * @return the Certificate object for this connection if the URL
+ * for it points to a JAR file entry, null otherwise.
+ *
+ * @exception IOException if getting the JAR entry causes an
+ * IOException to be thrown.
+ *
+ * @see #getJarEntry
+ */
+ public java.security.cert.Certificate[] getCertificates()
+ throws IOException
+ {
+ JarEntry e = getJarEntry();
+ return e != null ? e.getCertificates() : null;
+ }
+}
diff --git a/java/net/MalformedURLException.java b/java/net/MalformedURLException.java
new file mode 100644
index 0000000..7aef75c
--- /dev/null
+++ b/java/net/MalformedURLException.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+
+/**
+ * Thrown to indicate that a malformed URL has occurred. Either no
+ * legal protocol could be found in a specification string or the
+ * string could not be parsed.
+ *
+ * @author Arthur van Hoff
+ * @since JDK1.0
+ */
+public class MalformedURLException extends IOException {
+ private static final long serialVersionUID = -182787522200415866L;
+
+ /**
+ * Constructs a {@code MalformedURLException} with no detail message.
+ */
+ public MalformedURLException() {
+ }
+
+ /**
+ * Constructs a {@code MalformedURLException} with the
+ * specified detail message.
+ *
+ * @param msg the detail message.
+ */
+ public MalformedURLException(String msg) {
+ super(msg);
+ }
+}
diff --git a/java/net/MulticastSocket.java b/java/net/MulticastSocket.java
new file mode 100644
index 0000000..83a18c3
--- /dev/null
+++ b/java/net/MulticastSocket.java
@@ -0,0 +1,712 @@
+/*
+ * Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+// Android-changed: Updated example code to handle non-ASCII characters
+/**
+ * The multicast datagram socket class is useful for sending
+ * and receiving IP multicast packets. A MulticastSocket is
+ * a (UDP) DatagramSocket, with additional capabilities for
+ * joining "groups" of other multicast hosts on the internet.
+ * <P>
+ * A multicast group is specified by a class D IP address
+ * and by a standard UDP port number. Class D IP addresses
+ * are in the range <CODE>224.0.0.0</CODE> to <CODE>239.255.255.255</CODE>,
+ * inclusive. The address 224.0.0.0 is reserved and should not be used.
+ * <P>
+ * One would join a multicast group by first creating a MulticastSocket
+ * with the desired port, then invoking the
+ * <CODE>joinGroup(InetAddress groupAddr)</CODE>
+ * method:
+ * <PRE>
+ * // join a Multicast group and send the group salutations
+ * ...
+ * String msg = "Hello";
+ * InetAddress group = InetAddress.getByName("228.5.6.7");
+ * MulticastSocket s = new MulticastSocket(6789);
+ * s.joinGroup(group);
+ * byte[] bytes = msg.getBytes(StandardCharsets.UTF_8);
+ * DatagramPacket hi = new DatagramPacket(bytes, bytes.length,
+ * group, 6789);
+ * s.send(hi);
+ * // get their responses!
+ * byte[] buf = new byte[1000];
+ * DatagramPacket recv = new DatagramPacket(buf, buf.length);
+ * s.receive(recv);
+ * ...
+ * // OK, I'm done talking - leave the group...
+ * s.leaveGroup(group);
+ * </PRE>
+ *
+ * When one sends a message to a multicast group, <B>all</B> subscribing
+ * recipients to that host and port receive the message (within the
+ * time-to-live range of the packet, see below). The socket needn't
+ * be a member of the multicast group to send messages to it.
+ * <P>
+ * When a socket subscribes to a multicast group/port, it receives
+ * datagrams sent by other hosts to the group/port, as do all other
+ * members of the group and port. A socket relinquishes membership
+ * in a group by the leaveGroup(InetAddress addr) method. <B>
+ * Multiple MulticastSocket's</B> may subscribe to a multicast group
+ * and port concurrently, and they will all receive group datagrams.
+ * <P>
+ * Currently applets are not allowed to use multicast sockets.
+ *
+ * @author Pavani Diwanji
+ * @since JDK1.1
+ */
+public
+class MulticastSocket extends DatagramSocket {
+
+ /**
+ * Used on some platforms to record if an outgoing interface
+ * has been set for this socket.
+ */
+ private boolean interfaceSet;
+
+ /**
+ * Create a multicast socket.
+ *
+ * <p>If there is a security manager,
+ * its {@code checkListen} method is first called
+ * with 0 as its argument to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ * <p>
+ * When the socket is created the
+ * {@link DatagramSocket#setReuseAddress(boolean)} method is
+ * called to enable the SO_REUSEADDR socket option.
+ *
+ * @exception IOException if an I/O exception occurs
+ * while creating the MulticastSocket
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkListen} method doesn't allow the operation.
+ * @see SecurityManager#checkListen
+ * @see java.net.DatagramSocket#setReuseAddress(boolean)
+ */
+ public MulticastSocket() throws IOException {
+ this(new InetSocketAddress(0));
+ }
+
+ /**
+ * Create a multicast socket and bind it to a specific port.
+ *
+ * <p>If there is a security manager,
+ * its {@code checkListen} method is first called
+ * with the {@code port} argument
+ * as its argument to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ * <p>
+ * When the socket is created the
+ * {@link DatagramSocket#setReuseAddress(boolean)} method is
+ * called to enable the SO_REUSEADDR socket option.
+ *
+ * @param port port to use
+ * @exception IOException if an I/O exception occurs
+ * while creating the MulticastSocket
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkListen} method doesn't allow the operation.
+ * @see SecurityManager#checkListen
+ * @see java.net.DatagramSocket#setReuseAddress(boolean)
+ */
+ public MulticastSocket(int port) throws IOException {
+ this(new InetSocketAddress(port));
+ }
+
+ /**
+ * Create a MulticastSocket bound to the specified socket address.
+ * <p>
+ * Or, if the address is {@code null}, create an unbound socket.
+ *
+ * <p>If there is a security manager,
+ * its {@code checkListen} method is first called
+ * with the SocketAddress port as its argument to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ * <p>
+ * When the socket is created the
+ * {@link DatagramSocket#setReuseAddress(boolean)} method is
+ * called to enable the SO_REUSEADDR socket option.
+ *
+ * @param bindaddr Socket address to bind to, or {@code null} for
+ * an unbound socket.
+ * @exception IOException if an I/O exception occurs
+ * while creating the MulticastSocket
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkListen} method doesn't allow the operation.
+ * @see SecurityManager#checkListen
+ * @see java.net.DatagramSocket#setReuseAddress(boolean)
+ *
+ * @since 1.4
+ */
+ public MulticastSocket(SocketAddress bindaddr) throws IOException {
+ super((SocketAddress) null);
+
+ // Enable SO_REUSEADDR before binding
+ setReuseAddress(true);
+
+ if (bindaddr != null) {
+ try {
+ bind(bindaddr);
+ } finally {
+ if (!isBound())
+ close();
+ }
+ }
+ }
+
+ /**
+ * The lock on the socket's TTL. This is for set/getTTL and
+ * send(packet,ttl).
+ */
+ private Object ttlLock = new Object();
+
+ /**
+ * The lock on the socket's interface - used by setInterface
+ * and getInterface
+ */
+ private Object infLock = new Object();
+
+ /**
+ * The "last" interface set by setInterface on this MulticastSocket
+ */
+ private InetAddress infAddress = null;
+
+
+ /**
+ * Set the default time-to-live for multicast packets sent out
+ * on this {@code MulticastSocket} in order to control the
+ * scope of the multicasts.
+ *
+ * <p>The ttl is an <b>unsigned</b> 8-bit quantity, and so <B>must</B> be
+ * in the range {@code 0 <= ttl <= 0xFF }.
+ *
+ * @param ttl the time-to-live
+ * @exception IOException if an I/O exception occurs
+ * while setting the default time-to-live value
+ * @deprecated use the setTimeToLive method instead, which uses
+ * <b>int</b> instead of <b>byte</b> as the type for ttl.
+ * @see #getTTL()
+ */
+ @Deprecated
+ public void setTTL(byte ttl) throws IOException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ getImpl().setTTL(ttl);
+ }
+
+ /**
+ * Set the default time-to-live for multicast packets sent out
+ * on this {@code MulticastSocket} in order to control the
+ * scope of the multicasts.
+ *
+ * <P> The ttl <B>must</B> be in the range {@code 0 <= ttl <=
+ * 255} or an {@code IllegalArgumentException} will be thrown.
+ * Multicast packets sent with a TTL of {@code 0} are not transmitted
+ * on the network but may be delivered locally.
+ *
+ * @param ttl
+ * the time-to-live
+ *
+ * @throws IOException
+ * if an I/O exception occurs while setting the
+ * default time-to-live value
+ *
+ * @see #getTimeToLive()
+ */
+ public void setTimeToLive(int ttl) throws IOException {
+ if (ttl < 0 || ttl > 255) {
+ throw new IllegalArgumentException("ttl out of range");
+ }
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ getImpl().setTimeToLive(ttl);
+ }
+
+ /**
+ * Get the default time-to-live for multicast packets sent out on
+ * the socket.
+ *
+ * @exception IOException if an I/O exception occurs
+ * while getting the default time-to-live value
+ * @return the default time-to-live value
+ * @deprecated use the getTimeToLive method instead, which returns
+ * an <b>int</b> instead of a <b>byte</b>.
+ * @see #setTTL(byte)
+ */
+ @Deprecated
+ public byte getTTL() throws IOException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ return getImpl().getTTL();
+ }
+
+ /**
+ * Get the default time-to-live for multicast packets sent out on
+ * the socket.
+ * @exception IOException if an I/O exception occurs while
+ * getting the default time-to-live value
+ * @return the default time-to-live value
+ * @see #setTimeToLive(int)
+ */
+ public int getTimeToLive() throws IOException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ return getImpl().getTimeToLive();
+ }
+
+ /**
+ * Joins a multicast group. Its behavior may be affected by
+ * {@code setInterface} or {@code setNetworkInterface}.
+ *
+ * <p>If there is a security manager, this method first
+ * calls its {@code checkMulticast} method
+ * with the {@code mcastaddr} argument
+ * as its argument.
+ *
+ * @param mcastaddr is the multicast address to join
+ *
+ * @exception IOException if there is an error joining
+ * or when the address is not a multicast address.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkMulticast} method doesn't allow the join.
+ *
+ * @see SecurityManager#checkMulticast(InetAddress)
+ */
+ public void joinGroup(InetAddress mcastaddr) throws IOException {
+ if (isClosed()) {
+ throw new SocketException("Socket is closed");
+ }
+
+ checkAddress(mcastaddr, "joinGroup");
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkMulticast(mcastaddr);
+ }
+
+ if (!mcastaddr.isMulticastAddress()) {
+ throw new SocketException("Not a multicast address");
+ }
+
+ /**
+ * required for some platforms where it's not possible to join
+ * a group without setting the interface first.
+ */
+ NetworkInterface defaultInterface = NetworkInterface.getDefault();
+
+ if (!interfaceSet && defaultInterface != null) {
+ setNetworkInterface(defaultInterface);
+ }
+
+ getImpl().join(mcastaddr);
+ }
+
+ /**
+ * Leave a multicast group. Its behavior may be affected by
+ * {@code setInterface} or {@code setNetworkInterface}.
+ *
+ * <p>If there is a security manager, this method first
+ * calls its {@code checkMulticast} method
+ * with the {@code mcastaddr} argument
+ * as its argument.
+ *
+ * @param mcastaddr is the multicast address to leave
+ * @exception IOException if there is an error leaving
+ * or when the address is not a multicast address.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkMulticast} method doesn't allow the operation.
+ *
+ * @see SecurityManager#checkMulticast(InetAddress)
+ */
+ public void leaveGroup(InetAddress mcastaddr) throws IOException {
+ if (isClosed()) {
+ throw new SocketException("Socket is closed");
+ }
+
+ checkAddress(mcastaddr, "leaveGroup");
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkMulticast(mcastaddr);
+ }
+
+ if (!mcastaddr.isMulticastAddress()) {
+ throw new SocketException("Not a multicast address");
+ }
+
+ getImpl().leave(mcastaddr);
+ }
+
+ /**
+ * Joins the specified multicast group at the specified interface.
+ *
+ * <p>If there is a security manager, this method first
+ * calls its {@code checkMulticast} method
+ * with the {@code mcastaddr} argument
+ * as its argument.
+ *
+ * @param mcastaddr is the multicast address to join
+ * @param netIf specifies the local interface to receive multicast
+ * datagram packets, or <i>null</i> to defer to the interface set by
+ * {@link MulticastSocket#setInterface(InetAddress)} or
+ * {@link MulticastSocket#setNetworkInterface(NetworkInterface)}
+ *
+ * @exception IOException if there is an error joining
+ * or when the address is not a multicast address.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkMulticast} method doesn't allow the join.
+ * @throws IllegalArgumentException if mcastaddr is null or is a
+ * SocketAddress subclass not supported by this socket
+ *
+ * @see SecurityManager#checkMulticast(InetAddress)
+ * @since 1.4
+ */
+ public void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf)
+ throws IOException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+
+ if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress))
+ throw new IllegalArgumentException("Unsupported address type");
+
+ if (oldImpl)
+ throw new UnsupportedOperationException();
+
+ checkAddress(((InetSocketAddress)mcastaddr).getAddress(), "joinGroup");
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkMulticast(((InetSocketAddress)mcastaddr).getAddress());
+ }
+
+ if (!((InetSocketAddress)mcastaddr).getAddress().isMulticastAddress()) {
+ throw new SocketException("Not a multicast address");
+ }
+
+ getImpl().joinGroup(mcastaddr, netIf);
+ }
+
+ /**
+ * Leave a multicast group on a specified local interface.
+ *
+ * <p>If there is a security manager, this method first
+ * calls its {@code checkMulticast} method
+ * with the {@code mcastaddr} argument
+ * as its argument.
+ *
+ * @param mcastaddr is the multicast address to leave
+ * @param netIf specifies the local interface or <i>null</i> to defer
+ * to the interface set by
+ * {@link MulticastSocket#setInterface(InetAddress)} or
+ * {@link MulticastSocket#setNetworkInterface(NetworkInterface)}
+ * @exception IOException if there is an error leaving
+ * or when the address is not a multicast address.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkMulticast} method doesn't allow the operation.
+ * @throws IllegalArgumentException if mcastaddr is null or is a
+ * SocketAddress subclass not supported by this socket
+ *
+ * @see SecurityManager#checkMulticast(InetAddress)
+ * @since 1.4
+ */
+ public void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf)
+ throws IOException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+
+ if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress))
+ throw new IllegalArgumentException("Unsupported address type");
+
+ if (oldImpl)
+ throw new UnsupportedOperationException();
+
+ checkAddress(((InetSocketAddress)mcastaddr).getAddress(), "leaveGroup");
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkMulticast(((InetSocketAddress)mcastaddr).getAddress());
+ }
+
+ if (!((InetSocketAddress)mcastaddr).getAddress().isMulticastAddress()) {
+ throw new SocketException("Not a multicast address");
+ }
+
+ getImpl().leaveGroup(mcastaddr, netIf);
+ }
+
+ /**
+ * Set the multicast network interface used by methods
+ * whose behavior would be affected by the value of the
+ * network interface. Useful for multihomed hosts.
+ * @param inf the InetAddress
+ * @exception SocketException if there is an error in
+ * the underlying protocol, such as a TCP error.
+ * @see #getInterface()
+ */
+ public void setInterface(InetAddress inf) throws SocketException {
+ if (isClosed()) {
+ throw new SocketException("Socket is closed");
+ }
+ checkAddress(inf, "setInterface");
+ synchronized (infLock) {
+ getImpl().setOption(SocketOptions.IP_MULTICAST_IF, inf);
+ infAddress = inf;
+ interfaceSet = true;
+ }
+ }
+
+ /**
+ * Retrieve the address of the network interface used for
+ * multicast packets.
+ *
+ * @return An {@code InetAddress} representing
+ * the address of the network interface used for
+ * multicast packets.
+ *
+ * @exception SocketException if there is an error in
+ * the underlying protocol, such as a TCP error.
+ *
+ * @see #setInterface(java.net.InetAddress)
+ */
+ public InetAddress getInterface() throws SocketException {
+ if (isClosed()) {
+ throw new SocketException("Socket is closed");
+ }
+ synchronized (infLock) {
+ InetAddress ia =
+ (InetAddress)getImpl().getOption(SocketOptions.IP_MULTICAST_IF);
+
+ /**
+ * No previous setInterface or interface can be
+ * set using setNetworkInterface
+ */
+ if (infAddress == null) {
+ return ia;
+ }
+
+ /**
+ * Same interface set with setInterface?
+ */
+ if (ia.equals(infAddress)) {
+ return ia;
+ }
+
+ /**
+ * Different InetAddress from what we set with setInterface
+ * so enumerate the current interface to see if the
+ * address set by setInterface is bound to this interface.
+ */
+ try {
+ NetworkInterface ni = NetworkInterface.getByInetAddress(ia);
+ Enumeration<InetAddress> addrs = ni.getInetAddresses();
+ while (addrs.hasMoreElements()) {
+ InetAddress addr = addrs.nextElement();
+ if (addr.equals(infAddress)) {
+ return infAddress;
+ }
+ }
+
+ /**
+ * No match so reset infAddress to indicate that the
+ * interface has changed via means
+ */
+ infAddress = null;
+ return ia;
+ } catch (Exception e) {
+ return ia;
+ }
+ }
+ }
+
+ /**
+ * Specify the network interface for outgoing multicast datagrams
+ * sent on this socket.
+ *
+ * @param netIf the interface
+ * @exception SocketException if there is an error in
+ * the underlying protocol, such as a TCP error.
+ * @see #getNetworkInterface()
+ * @since 1.4
+ */
+ public void setNetworkInterface(NetworkInterface netIf)
+ throws SocketException {
+
+ synchronized (infLock) {
+ getImpl().setOption(SocketOptions.IP_MULTICAST_IF2, netIf);
+ infAddress = null;
+ interfaceSet = true;
+ }
+ }
+
+ /**
+ * Get the multicast network interface set.
+ *
+ * @exception SocketException if there is an error in
+ * the underlying protocol, such as a TCP error.
+ * @return the multicast {@code NetworkInterface} currently set
+ * @see #setNetworkInterface(NetworkInterface)
+ * @since 1.4
+ */
+ public NetworkInterface getNetworkInterface() throws SocketException {
+ // Android-changed: Support Integer IP_MULTICAST_IF2 values for app compat.
+ Integer niIndex
+ = (Integer)getImpl().getOption(SocketOptions.IP_MULTICAST_IF2);
+ if (niIndex == 0) {
+ InetAddress[] addrs = new InetAddress[1];
+ addrs[0] = InetAddress.anyLocalAddress();
+ return new NetworkInterface(addrs[0].getHostName(), 0, addrs);
+ } else {
+ return NetworkInterface.getByIndex(niIndex);
+ }
+ }
+
+ /**
+ * Disable/Enable local loopback of multicast datagrams
+ * The option is used by the platform's networking code as a hint
+ * for setting whether multicast data will be looped back to
+ * the local socket.
+ *
+ * <p>Because this option is a hint, applications that want to
+ * verify what loopback mode is set to should call
+ * {@link #getLoopbackMode()}
+ * @param disable {@code true} to disable the LoopbackMode
+ * @throws SocketException if an error occurs while setting the value
+ * @since 1.4
+ * @see #getLoopbackMode
+ */
+ public void setLoopbackMode(boolean disable) throws SocketException {
+ getImpl().setOption(SocketOptions.IP_MULTICAST_LOOP, Boolean.valueOf(disable));
+ }
+
+ /**
+ * Get the setting for local loopback of multicast datagrams.
+ *
+ * @throws SocketException if an error occurs while getting the value
+ * @return true if the LoopbackMode has been disabled
+ * @since 1.4
+ * @see #setLoopbackMode
+ */
+ public boolean getLoopbackMode() throws SocketException {
+ return ((Boolean)getImpl().getOption(SocketOptions.IP_MULTICAST_LOOP)).booleanValue();
+ }
+
+ /**
+ * Sends a datagram packet to the destination, with a TTL (time-
+ * to-live) other than the default for the socket. This method
+ * need only be used in instances where a particular TTL is desired;
+ * otherwise it is preferable to set a TTL once on the socket, and
+ * use that default TTL for all packets. This method does <B>not
+ * </B> alter the default TTL for the socket. Its behavior may be
+ * affected by {@code setInterface}.
+ *
+ * <p>If there is a security manager, this method first performs some
+ * security checks. First, if {@code p.getAddress().isMulticastAddress()}
+ * is true, this method calls the
+ * security manager's {@code checkMulticast} method
+ * with {@code p.getAddress()} and {@code ttl} as its arguments.
+ * If the evaluation of that expression is false,
+ * this method instead calls the security manager's
+ * {@code checkConnect} method with arguments
+ * {@code p.getAddress().getHostAddress()} and
+ * {@code p.getPort()}. Each call to a security manager method
+ * could result in a SecurityException if the operation is not allowed.
+ *
+ * @param p is the packet to be sent. The packet should contain
+ * the destination multicast ip address and the data to be sent.
+ * One does not need to be the member of the group to send
+ * packets to a destination multicast address.
+ * @param ttl optional time to live for multicast packet.
+ * default ttl is 1.
+ *
+ * @exception IOException is raised if an error occurs i.e
+ * error while setting ttl.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkMulticast} or {@code checkConnect}
+ * method doesn't allow the send.
+ *
+ * @deprecated Use the following code or its equivalent instead:
+ * ......
+ * int ttl = mcastSocket.getTimeToLive();
+ * mcastSocket.setTimeToLive(newttl);
+ * mcastSocket.send(p);
+ * mcastSocket.setTimeToLive(ttl);
+ * ......
+ *
+ * @see DatagramSocket#send
+ * @see DatagramSocket#receive
+ * @see SecurityManager#checkMulticast(java.net.InetAddress, byte)
+ * @see SecurityManager#checkConnect
+ */
+ @Deprecated
+ public void send(DatagramPacket p, byte ttl)
+ throws IOException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ checkAddress(p.getAddress(), "send");
+ synchronized(ttlLock) {
+ synchronized(p) {
+ if (connectState == ST_NOT_CONNECTED) {
+ // Security manager makes sure that the multicast address
+ // is allowed one and that the ttl used is less
+ // than the allowed maxttl.
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ if (p.getAddress().isMulticastAddress()) {
+ security.checkMulticast(p.getAddress(), ttl);
+ } else {
+ security.checkConnect(p.getAddress().getHostAddress(),
+ p.getPort());
+ }
+ }
+ } else {
+ // we're connected
+ InetAddress packetAddress = null;
+ packetAddress = p.getAddress();
+ if (packetAddress == null) {
+ p.setAddress(connectedAddress);
+ p.setPort(connectedPort);
+ } else if ((!packetAddress.equals(connectedAddress)) ||
+ p.getPort() != connectedPort) {
+ throw new SecurityException("connected address and packet address" +
+ " differ");
+ }
+ }
+ byte dttl = getTTL();
+ try {
+ if (ttl != dttl) {
+ // set the ttl
+ getImpl().setTTL(ttl);
+ }
+ // call the datagram method to send
+ getImpl().send(p);
+ } finally {
+ // set it back to default
+ if (ttl != dttl) {
+ getImpl().setTTL(dttl);
+ }
+ }
+ } // synch p
+ } //synch ttl
+ } //method
+}
diff --git a/java/net/NetPermission.java b/java/net/NetPermission.java
new file mode 100644
index 0000000..b5a0eab
--- /dev/null
+++ b/java/net/NetPermission.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.security.*;
+
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
+/**
+ * Legacy security code; do not use.
+ */
+
+public final class NetPermission extends BasicPermission {
+
+ public NetPermission(String name)
+ {
+ super("");
+ }
+
+ public NetPermission(String name, String actions)
+ {
+ super("", "");
+ }
+}
diff --git a/java/net/NetworkInterface.java b/java/net/NetworkInterface.java
new file mode 100644
index 0000000..a30b6bf
--- /dev/null
+++ b/java/net/NetworkInterface.java
@@ -0,0 +1,748 @@
+/*
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import android.system.ErrnoException;
+
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import android.system.StructIfaddrs;
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+import sun.security.action.*;
+import java.security.AccessController;
+
+import static android.system.OsConstants.*;
+
+// Android-note: NetworkInterface has been rewritten to avoid native code.
+// Fix upstream bug not returning link-down interfaces. http://b/26238832
+// Android-added: Document restrictions for targetSdkVersion >= R. http://b/141455849
+/**
+ * This class represents a Network Interface made up of a name,
+ * and a list of IP addresses assigned to this interface.
+ * It is used to identify the local interface on which a multicast group
+ * is joined.
+ *
+ * Interfaces are normally known by names such as "le0".
+ * <p>
+ * <a name="access-restrictions"></a>Note that information about
+ * {@link NetworkInterface}s may be restricted. For example, non-system apps
+ * with {@code targetSdkVersion >= android.os.Build.VERSION_CODES.R} will only
+ * have access to information about {@link NetworkInterface}s that are
+ * associated with an {@link InetAddress}.
+ *
+ * @since 1.4
+ */
+public final class NetworkInterface {
+ private String name;
+ private String displayName;
+ private int index;
+ private InetAddress addrs[];
+ private InterfaceAddress bindings[];
+ // Android-changed: Rewrote NetworkInterface on top of Libcore.io.
+ // private NetworkInterface childs[];
+ private List<NetworkInterface> childs;
+ private NetworkInterface parent = null;
+ private boolean virtual = false;
+ private static final NetworkInterface defaultInterface;
+ private static final int defaultIndex; /* index of defaultInterface */
+
+ // Android-changed: Fix upstream bug not returning link-down interfaces. http://b/26238832
+ private byte[] hardwareAddr;
+
+ static {
+ // Android-removed: Android doesn't need to call native init.
+ /*
+ AccessController.doPrivileged(
+ new java.security.PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("net");
+ return null;
+ }
+ });
+
+ init();
+ */
+ defaultInterface = DefaultInterface.getDefault();
+ if (defaultInterface != null) {
+ defaultIndex = defaultInterface.getIndex();
+ } else {
+ defaultIndex = 0;
+ }
+ }
+
+ /**
+ * Returns an NetworkInterface object with index set to 0 and name to null.
+ * Setting such an interface on a MulticastSocket will cause the
+ * kernel to choose one interface for sending multicast packets.
+ *
+ */
+ NetworkInterface() {
+ }
+
+ NetworkInterface(String name, int index, InetAddress[] addrs) {
+ this.name = name;
+ this.index = index;
+ this.addrs = addrs;
+ }
+
+ /**
+ * Get the name of this network interface.
+ *
+ * @return the name of this network interface
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Convenience method to return an Enumeration with all or a
+ * subset of the InetAddresses bound to this network interface.
+ * <p>
+ * If there is a security manager, its {@code checkConnect}
+ * method is called for each InetAddress. Only InetAddresses where
+ * the {@code checkConnect} doesn't throw a SecurityException
+ * will be returned in the Enumeration. However, if the caller has the
+ * {@link NetPermission}("getNetworkInformation") permission, then all
+ * InetAddresses are returned.
+ * @return an Enumeration object with all or a subset of the InetAddresses
+ * bound to this network interface
+ */
+ public Enumeration<InetAddress> getInetAddresses() {
+
+ class checkedAddresses implements Enumeration<InetAddress> {
+
+ private int i=0, count=0;
+ private InetAddress local_addrs[];
+
+ checkedAddresses() {
+ local_addrs = new InetAddress[addrs.length];
+ boolean trusted = true;
+
+ SecurityManager sec = System.getSecurityManager();
+ if (sec != null) {
+ try {
+ sec.checkPermission(new NetPermission("getNetworkInformation"));
+ } catch (SecurityException e) {
+ trusted = false;
+ }
+ }
+ for (int j=0; j<addrs.length; j++) {
+ try {
+ if (sec != null && !trusted) {
+ sec.checkConnect(addrs[j].getHostAddress(), -1);
+ }
+ local_addrs[count++] = addrs[j];
+ } catch (SecurityException e) { }
+ }
+
+ }
+
+ public InetAddress nextElement() {
+ if (i < count) {
+ return local_addrs[i++];
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+
+ public boolean hasMoreElements() {
+ return (i < count);
+ }
+ }
+ return new checkedAddresses();
+
+ }
+
+ /**
+ * Get a List of all or a subset of the {@code InterfaceAddresses}
+ * of this network interface.
+ * <p>
+ * If there is a security manager, its {@code checkConnect}
+ * method is called with the InetAddress for each InterfaceAddress.
+ * Only InterfaceAddresses where the {@code checkConnect} doesn't throw
+ * a SecurityException will be returned in the List.
+ *
+ * @return a {@code List} object with all or a subset of the
+ * InterfaceAddresss of this network interface
+ * @since 1.6
+ */
+ public java.util.List<InterfaceAddress> getInterfaceAddresses() {
+ java.util.List<InterfaceAddress> lst = new java.util.ArrayList<InterfaceAddress>(1);
+ // BEGIN Android-changed: Cherry-picked upstream OpenJDK9 change rev 59a110a38cea
+ // http://b/30628919
+ if (bindings != null) {
+ SecurityManager sec = System.getSecurityManager();
+ for (int j=0; j<bindings.length; j++) {
+ try {
+ if (sec != null) {
+ sec.checkConnect(bindings[j].getAddress().getHostAddress(), -1);
+ }
+ lst.add(bindings[j]);
+ } catch (SecurityException e) { }
+ }
+ }
+ // END Android-changed: Cherry-picked upstream OpenJDK9 change rev 59a110a38cea
+ return lst;
+ }
+
+ /**
+ * Get an Enumeration with all the subinterfaces (also known as virtual
+ * interfaces) attached to this network interface.
+ * <p>
+ * For instance eth0:1 will be a subinterface to eth0.
+ *
+ * @return an Enumeration object with all of the subinterfaces
+ * of this network interface
+ * @since 1.6
+ */
+ public Enumeration<NetworkInterface> getSubInterfaces() {
+ // Android-changed: Rewrote NetworkInterface on top of Libcore.io.
+ return Collections.enumeration(childs);
+ }
+
+ /**
+ * Returns the parent NetworkInterface of this interface if this is
+ * a subinterface, or {@code null} if it is a physical
+ * (non virtual) interface or has no parent.
+ *
+ * @return The {@code NetworkInterface} this interface is attached to.
+ * @since 1.6
+ */
+ public NetworkInterface getParent() {
+ return parent;
+ }
+
+ /**
+ * Returns the index of this network interface. The index is an integer greater
+ * or equal to zero, or {@code -1} for unknown. This is a system specific value
+ * and interfaces with the same name can have different indexes on different
+ * machines.
+ *
+ * @return the index of this network interface or {@code -1} if the index is
+ * unknown
+ * @see #getByIndex(int)
+ * @since 1.7
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * Get the display name of this network interface.
+ * A display name is a human readable String describing the network
+ * device.
+ *
+ * @return a non-empty string representing the display name of this network
+ * interface, or null if no display name is available.
+ */
+ public String getDisplayName() {
+ /* strict TCK conformance */
+ return "".equals(displayName) ? null : displayName;
+ }
+
+ // Android-added: Document restrictions for targetSdkVersion >= R. http://b/141455849
+ /**
+ * Searches for the network interface with the specified name.
+ *
+ * @param name
+ * The name of the network interface.
+ *
+ * @return A {@code NetworkInterface} with the specified name,
+ * or {@code null} if the network interface with the specified
+ * name does not exist or <a href="#access-restrictions">can't be
+ * accessed</a>.
+ *
+ * @throws SocketException
+ * If an I/O error occurs.
+ *
+ * @throws NullPointerException
+ * If the specified name is {@code null}.
+ */
+ public static NetworkInterface getByName(String name) throws SocketException {
+ if (name == null)
+ throw new NullPointerException();
+
+ // Android-changed: Rewrote NetworkInterface on top of Libcore.io.
+ NetworkInterface[] nis = getAll();
+ for (NetworkInterface ni : nis) {
+ if (ni.getName().equals(name)) {
+ return ni;
+ }
+ }
+ return null;
+ }
+
+ // Android-added: Document restrictions for targetSdkVersion >= R. http://b/141455849
+ /**
+ * Get a network interface given its index.
+ *
+ * @param index an integer, the index of the interface
+ * @return the NetworkInterface obtained from its index, or {@code null} if
+ * an interface with the specified index does not exist or
+ * <a href="#access-restrictions">can't be accessed</a>.
+ * @throws SocketException if an I/O error occurs.
+ * @throws IllegalArgumentException if index has a negative value
+ * @see #getIndex()
+ * @since 1.7
+ */
+ public static NetworkInterface getByIndex(int index) throws SocketException {
+ if (index < 0)
+ throw new IllegalArgumentException("Interface index can't be negative");
+
+ // Android-changed: Rewrote NetworkInterface on top of Libcore.io.
+ NetworkInterface[] nis = getAll();
+ for (NetworkInterface ni : nis) {
+ if (ni.getIndex() == index) {
+ return ni;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Convenience method to search for a network interface that
+ * has the specified Internet Protocol (IP) address bound to
+ * it.
+ * <p>
+ * If the specified IP address is bound to multiple network
+ * interfaces it is not defined which network interface is
+ * returned.
+ *
+ * @param addr
+ * The {@code InetAddress} to search with.
+ *
+ * @return A {@code NetworkInterface}
+ * or {@code null} if there is no network interface
+ * with the specified IP address.
+ *
+ * @throws SocketException
+ * If an I/O error occurs.
+ *
+ * @throws NullPointerException
+ * If the specified address is {@code null}.
+ */
+ public static NetworkInterface getByInetAddress(InetAddress addr) throws SocketException {
+ if (addr == null) {
+ throw new NullPointerException();
+ }
+ if (!(addr instanceof Inet4Address || addr instanceof Inet6Address)) {
+ throw new IllegalArgumentException ("invalid address type");
+ }
+
+ // Android-changed: Rewrote NetworkInterface on top of Libcore.io.
+ NetworkInterface[] nis = getAll();
+ for (NetworkInterface ni : nis) {
+ for (InetAddress inetAddress : Collections.list(ni.getInetAddresses())) {
+ if (inetAddress.equals(addr)) {
+ return ni;
+ }
+ }
+ }
+ return null;
+ }
+
+ // Android-added: Document restrictions for targetSdkVersion >= R. http://b/141455849
+ /**
+ * Returns all the interfaces on this machine. The {@code Enumeration}
+ * contains at least one element, possibly representing a loopback
+ * interface that only supports communication between entities on
+ * this machine.
+ *
+ * NOTE: can use getNetworkInterfaces()+getInetAddresses()
+ * to obtain all IP addresses for this node
+ * <p>
+ * For non-system apps with
+ * {@code targetSdkVersion >= android.os.Build.VERSION_CODES.R}, this
+ * method will only return information for {@link NetworkInterface}s that
+ * are associated with an {@link InetAddress}.
+ *
+ * @return an Enumeration of NetworkInterfaces found on this machine
+ * that <a href="#access-restrictions">are accessible</a>.
+ * @exception SocketException if an I/O error occurs.
+ */
+
+ public static Enumeration<NetworkInterface> getNetworkInterfaces()
+ throws SocketException {
+ final NetworkInterface[] netifs = getAll();
+ // Android-changed: Rewrote NetworkInterface on top of Libcore.io.
+ // // specified to return null if no network interfaces
+ // if (netifs == null)
+ if (netifs.length == 0)
+ return null;
+
+ // Android-changed: Rewrote NetworkInterface on top of Libcore.io.
+ /*
+ return new Enumeration<NetworkInterface>() {
+ private int i = 0;
+ public NetworkInterface nextElement() {
+ if (netifs != null && i < netifs.length) {
+ NetworkInterface netif = netifs[i++];
+ return netif;
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+
+ public boolean hasMoreElements() {
+ return (netifs != null && i < netifs.length);
+ }
+ };
+ */
+ return Collections.enumeration(Arrays.asList(netifs));
+ }
+
+ // BEGIN Android-changed: Rewrote NetworkInterface on top of Libcore.io.
+ // private native static NetworkInterface[] getAll()
+ // throws SocketException;
+ private static NetworkInterface[] getAll() throws SocketException {
+ // Group Ifaddrs by interface name.
+ Map<String, List<StructIfaddrs>> inetMap = new HashMap<>();
+
+ StructIfaddrs[] ifaddrs;
+ try {
+ ifaddrs = Libcore.os.getifaddrs();
+ } catch (ErrnoException e) {
+ throw e.rethrowAsSocketException();
+ }
+
+ for (StructIfaddrs ifa : ifaddrs) {
+ String name = ifa.ifa_name;
+
+ List<StructIfaddrs> ifas;
+ if ((ifas = inetMap.get(name)) == null) {
+ ifas = new ArrayList<>();
+ inetMap.put(name, ifas);
+ }
+
+ ifas.add(ifa);
+ }
+
+ // Populate NetworkInterface instances.
+ Map<String, NetworkInterface> nis = new HashMap<>(inetMap.size());
+ for (Map.Entry<String, List<StructIfaddrs>> e : inetMap.entrySet()) {
+ String name = e.getKey();
+ int index = Libcore.os.if_nametoindex(e.getKey());
+ if (index == 0) {
+ // This interface has gone away between getifaddrs and if_nametoindex
+ continue;
+ }
+
+ NetworkInterface ni = new NetworkInterface(name, index, null);
+ ni.displayName = name;
+
+ List<InetAddress> addrs = new ArrayList<>();
+ List<InterfaceAddress> binds = new ArrayList<>();
+
+ for (StructIfaddrs ifa : e.getValue()) {
+ if (ifa.ifa_addr != null) {
+ addrs.add(ifa.ifa_addr);
+ binds.add(new InterfaceAddress(ifa.ifa_addr, (Inet4Address) ifa.ifa_broadaddr,
+ ifa.ifa_netmask));
+ }
+
+ if (ifa.hwaddr != null) {
+ ni.hardwareAddr = ifa.hwaddr;
+ }
+ }
+
+ ni.addrs = addrs.toArray(new InetAddress[addrs.size()]);
+ ni.bindings = binds.toArray(new InterfaceAddress[binds.size()]);
+ ni.childs = new ArrayList<>(0);
+ nis.put(name, ni);
+ }
+
+ // Populate childs/parent.
+ for (Map.Entry<String, NetworkInterface> e : nis.entrySet()) {
+ NetworkInterface ni = e.getValue();
+ String niName = ni.getName();
+ int colonIdx = niName.indexOf(':');
+ if (colonIdx != -1) {
+ // This is a virtual interface.
+ String parentName = niName.substring(0, colonIdx);
+ NetworkInterface parent = nis.get(parentName);
+
+ ni.virtual = true;
+ ni.parent = parent;
+ parent.childs.add(ni);
+ }
+ }
+
+ return nis.values().toArray(new NetworkInterface[nis.size()]);
+ }
+ // END Android-changed: Rewrote NetworkInterface on top of Libcore.io.
+
+ /**
+ * Returns whether a network interface is up and running.
+ *
+ * @return {@code true} if the interface is up and running.
+ * @exception SocketException if an I/O error occurs.
+ * @since 1.6
+ */
+
+ public boolean isUp() throws SocketException {
+ // Android-changed: Rewrote NetworkInterface on top of Libcore.io.
+ final int mask = IFF_UP | IFF_RUNNING;
+ return (getFlags() & mask) == mask;
+ }
+
+ /**
+ * Returns whether a network interface is a loopback interface.
+ *
+ * @return {@code true} if the interface is a loopback interface.
+ * @exception SocketException if an I/O error occurs.
+ * @since 1.6
+ */
+
+ public boolean isLoopback() throws SocketException {
+ // Android-changed: Rewrote NetworkInterface on top of Libcore.io.
+ return (getFlags() & IFF_LOOPBACK) != 0;
+ }
+
+ /**
+ * Returns whether a network interface is a point to point interface.
+ * A typical point to point interface would be a PPP connection through
+ * a modem.
+ *
+ * @return {@code true} if the interface is a point to point
+ * interface.
+ * @exception SocketException if an I/O error occurs.
+ * @since 1.6
+ */
+
+ public boolean isPointToPoint() throws SocketException {
+ // Android-changed: Rewrote NetworkInterface on top of Libcore.io.
+ return (getFlags() & IFF_POINTOPOINT) != 0;
+ }
+
+ /**
+ * Returns whether a network interface supports multicasting or not.
+ *
+ * @return {@code true} if the interface supports Multicasting.
+ * @exception SocketException if an I/O error occurs.
+ * @since 1.6
+ */
+
+ public boolean supportsMulticast() throws SocketException {
+ // Android-changed: Rewrote NetworkInterface on top of Libcore.io.
+ return (getFlags() & IFF_MULTICAST) != 0;
+ }
+
+ // Android-added: Document restrictions for targetSdkVersion >= R. http://b/141455849
+ /**
+ * Returns the hardware address (usually MAC) of the interface if it
+ * has one and if it can be accessed given the current privileges.
+ * If a security manager is set, then the caller must have
+ * the permission {@link NetPermission}("getNetworkInformation").
+ *
+ * @return a byte array containing the address, or {@code null} if
+ * the address doesn't exist, is not accessible or a security
+ * manager is set and the caller does not have the permission
+ * NetPermission("getNetworkInformation"). For example, this
+ * method will generally return {@code null} when called by
+ * non-system apps having
+ * {@code targetSdkVersion >= android.os.Build.VERSION_CODES.R}.
+ *
+ * @exception SocketException if an I/O error occurs.
+ * @since 1.6
+ */
+ public byte[] getHardwareAddress() throws SocketException {
+ // BEGIN Android-changed: Fix upstream not returning link-down interfaces. http://b/26238832
+ /*
+ for (InetAddress addr : addrs) {
+ if (addr instanceof Inet4Address) {
+ return getMacAddr0(((Inet4Address)addr).getAddress(), name, index);
+ }
+ }
+ return getMacAddr0(null, name, index);
+ */
+ NetworkInterface ni = getByName(name);
+ if (ni == null) {
+ throw new SocketException("NetworkInterface doesn't exist anymore");
+ }
+ return ni.hardwareAddr;
+ // END Android-changed: Fix upstream not returning link-down interfaces. http://b/26238832
+ }
+
+ /**
+ * Returns the Maximum Transmission Unit (MTU) of this interface.
+ *
+ * @return the value of the MTU for that interface.
+ * @exception SocketException if an I/O error occurs.
+ * @since 1.6
+ */
+ public int getMTU() throws SocketException {
+ // Android-changed: Rewrote NetworkInterface on top of Libcore.io.
+ // return getMTU0(name, index);
+ FileDescriptor fd = null;
+ try {
+ fd = Libcore.rawOs.socket(AF_INET, SOCK_DGRAM, 0);
+ return Libcore.rawOs.ioctlMTU(fd, name);
+ } catch (ErrnoException e) {
+ throw e.rethrowAsSocketException();
+ } catch (Exception ex) {
+ throw new SocketException(ex);
+ } finally {
+ IoUtils.closeQuietly(fd);
+ }
+ }
+
+ /**
+ * Returns whether this interface is a virtual interface (also called
+ * subinterface).
+ * Virtual interfaces are, on some systems, interfaces created as a child
+ * of a physical interface and given different settings (like address or
+ * MTU). Usually the name of the interface will the name of the parent
+ * followed by a colon (:) and a number identifying the child since there
+ * can be several virtual interfaces attached to a single physical
+ * interface.
+ *
+ * @return {@code true} if this interface is a virtual interface.
+ * @since 1.6
+ */
+ public boolean isVirtual() {
+ return virtual;
+ }
+
+ // BEGIN Android-removed: Rewrote NetworkInterface on top of Libcore.io.
+ /*
+ private native static boolean isUp0(String name, int ind) throws SocketException;
+ private native static boolean isLoopback0(String name, int ind) throws SocketException;
+ private native static boolean supportsMulticast0(String name, int ind) throws SocketException;
+ private native static boolean isP2P0(String name, int ind) throws SocketException;
+ private native static byte[] getMacAddr0(byte[] inAddr, String name, int ind) throws SocketException;
+ private native static int getMTU0(String name, int ind) throws SocketException;
+ */
+ // END Android-removed: Rewrote NetworkInterface on top of Libcore.io.
+
+ // BEGIN Android-added: Rewrote NetworkInterface on top of Libcore.io.
+ private int getFlags() throws SocketException {
+ FileDescriptor fd = null;
+ try {
+ fd = Libcore.rawOs.socket(AF_INET, SOCK_DGRAM, 0);
+ return Libcore.rawOs.ioctlFlags(fd, name);
+ } catch (ErrnoException e) {
+ throw e.rethrowAsSocketException();
+ } catch (Exception ex) {
+ throw new SocketException(ex);
+ } finally {
+ IoUtils.closeQuietly(fd);
+ }
+ }
+ // END Android-added: Rewrote NetworkInterface on top of Libcore.io.
+
+ /**
+ * Compares this object against the specified object.
+ * The result is {@code true} if and only if the argument is
+ * not {@code null} and it represents the same NetworkInterface
+ * as this object.
+ * <p>
+ * Two instances of {@code NetworkInterface} represent the same
+ * NetworkInterface if both name and addrs are the same for both.
+ *
+ * @param obj the object to compare against.
+ * @return {@code true} if the objects are the same;
+ * {@code false} otherwise.
+ * @see java.net.InetAddress#getAddress()
+ */
+ public boolean equals(Object obj) {
+ if (!(obj instanceof NetworkInterface)) {
+ return false;
+ }
+ NetworkInterface that = (NetworkInterface)obj;
+ if (this.name != null ) {
+ if (!this.name.equals(that.name)) {
+ return false;
+ }
+ } else {
+ if (that.name != null) {
+ return false;
+ }
+ }
+
+ if (this.addrs == null) {
+ return that.addrs == null;
+ } else if (that.addrs == null) {
+ return false;
+ }
+
+ /* Both addrs not null. Compare number of addresses */
+
+ if (this.addrs.length != that.addrs.length) {
+ return false;
+ }
+
+ InetAddress[] thatAddrs = that.addrs;
+ int count = thatAddrs.length;
+
+ for (int i=0; i<count; i++) {
+ boolean found = false;
+ for (int j=0; j<count; j++) {
+ if (addrs[i].equals(thatAddrs[j])) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public int hashCode() {
+ return name == null? 0: name.hashCode();
+ }
+
+ public String toString() {
+ String result = "name:";
+ result += name == null? "null": name;
+ if (displayName != null) {
+ result += " (" + displayName + ")";
+ }
+ return result;
+ }
+
+ // Android-removed: Android doesn't need to call native init.
+ // private static native void init();
+
+ /**
+ * Returns the default network interface of this system
+ *
+ * @return the default interface
+ */
+ static NetworkInterface getDefault() {
+ return defaultInterface;
+ }
+}
diff --git a/java/net/NoRouteToHostException.java b/java/net/NoRouteToHostException.java
new file mode 100644
index 0000000..76f0814
--- /dev/null
+++ b/java/net/NoRouteToHostException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * Signals that an error occurred while attempting to connect a
+ * socket to a remote address and port. Typically, the remote
+ * host cannot be reached because of an intervening firewall, or
+ * if an intermediate router is down.
+ *
+ * @since JDK1.1
+ */
+public class NoRouteToHostException extends SocketException {
+ private static final long serialVersionUID = -1897550894873493790L;
+
+ /**
+ * Constructs a new NoRouteToHostException with the specified detail
+ * message as to why the remote host cannot be reached.
+ * A detail message is a String that gives a specific
+ * description of this error.
+ * @param msg the detail message
+ */
+ public NoRouteToHostException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Construct a new NoRouteToHostException with no detailed message.
+ */
+ public NoRouteToHostException() {}
+}
diff --git a/java/net/PasswordAuthentication.java b/java/net/PasswordAuthentication.java
new file mode 100644
index 0000000..5529568
--- /dev/null
+++ b/java/net/PasswordAuthentication.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+
+/**
+ * The class PasswordAuthentication is a data holder that is used by
+ * Authenticator. It is simply a repository for a user name and a password.
+ *
+ * @see java.net.Authenticator
+ * @see java.net.Authenticator#getPasswordAuthentication()
+ *
+ * @author Bill Foote
+ * @since 1.2
+ */
+
+public final class PasswordAuthentication {
+
+ private String userName;
+ private char[] password;
+
+ /**
+ * Creates a new {@code PasswordAuthentication} object from the given
+ * user name and password.
+ *
+ * <p> Note that the given user password is cloned before it is stored in
+ * the new {@code PasswordAuthentication} object.
+ *
+ * @param userName the user name
+ * @param password the user's password
+ */
+ public PasswordAuthentication(String userName, char[] password) {
+ this.userName = userName;
+ this.password = password.clone();
+ }
+
+ /**
+ * Returns the user name.
+ *
+ * @return the user name
+ */
+ public String getUserName() {
+ return userName;
+ }
+
+ /**
+ * Returns the user password.
+ *
+ * <p> Note that this method returns a reference to the password. It is
+ * the caller's responsibility to zero out the password information after
+ * it is no longer needed.
+ *
+ * @return the password
+ */
+ public char[] getPassword() {
+ return password;
+ }
+}
diff --git a/java/net/PlainDatagramSocketImpl.java b/java/net/PlainDatagramSocketImpl.java
new file mode 100644
index 0000000..d4b1f2c
--- /dev/null
+++ b/java/net/PlainDatagramSocketImpl.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2007,2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.net;
+
+import android.system.ErrnoException;
+import android.system.StructGroupReq;
+
+import java.io.IOException;
+import libcore.io.IoBridge;
+import libcore.io.Libcore;
+import libcore.util.EmptyArray;
+
+import jdk.net.*;
+
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.AF_UNSPEC;
+import static android.system.OsConstants.IPPROTO_IP;
+import static android.system.OsConstants.IP_MULTICAST_ALL;
+import static android.system.OsConstants.MSG_PEEK;
+import static android.system.OsConstants.POLLERR;
+import static android.system.OsConstants.POLLIN;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static libcore.io.IoBridge.JAVA_IP_MULTICAST_TTL;
+import static libcore.io.IoBridge.JAVA_MCAST_JOIN_GROUP;
+import static libcore.io.IoBridge.JAVA_MCAST_LEAVE_GROUP;
+import static sun.net.ExtendedOptionsImpl.*;
+
+/*
+ * On Unix systems we simply delegate to native methods.
+ *
+ * @author Chris Hegarty
+ */
+
+class PlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl
+{
+ // Android-removed: init method has been removed
+ // static {
+ // init();
+ // }
+
+ protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
+ if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) {
+ super.setOption(name, value);
+ } else {
+ if (isClosed()) {
+ throw new SocketException("Socket closed");
+ }
+ checkSetOptionPermission(name);
+ checkValueType(value, SocketFlow.class);
+ setFlowOption(getFileDescriptor(), (SocketFlow)value);
+ }
+ }
+
+ protected <T> T getOption(SocketOption<T> name) throws IOException {
+ if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) {
+ return super.getOption(name);
+ }
+ if (isClosed()) {
+ throw new SocketException("Socket closed");
+ }
+ checkGetOptionPermission(name);
+ SocketFlow flow = SocketFlow.create();
+ getFlowOption(getFileDescriptor(), flow);
+ return (T)flow;
+ }
+
+ protected void socketSetOption(int opt, Object val) throws SocketException {
+ try {
+ socketSetOption0(opt, val);
+ } catch (SocketException se) {
+ if (!connected)
+ throw se;
+ }
+ }
+
+ // BEGIN Android-changed: Rewrote on top of Libcore.io.
+ protected synchronized void bind0(int lport, InetAddress laddr) throws SocketException {
+ if (isClosed()) {
+ throw new SocketException("Socket closed");
+ }
+
+ IoBridge.bind(fd, laddr, lport);
+
+ if (lport == 0) {
+ // Now that we're a connected socket, let's extract the port number that the system
+ // chose for us and store it in the Socket object.
+ localPort = IoBridge.getLocalInetSocketAddress(fd).getPort();
+ } else {
+ localPort = lport;
+ }
+ }
+
+ protected void send(DatagramPacket p) throws IOException {
+ if (isClosed()) {
+ throw new SocketException("Socket closed");
+ }
+ if (p.getData() == null || p.getAddress() == null) {
+ throw new NullPointerException("null buffer || null address");
+ }
+
+ int port = connected ? 0 : p.getPort();
+ InetAddress address = connected ? null : p.getAddress();
+ IoBridge.sendto(fd, p.getData(), p.getOffset(), p.getLength(), 0, address, port);
+ }
+
+ protected synchronized int peek(InetAddress i) throws IOException {
+ DatagramPacket p = new DatagramPacket(EmptyArray.BYTE, 0);
+ doRecv(p, MSG_PEEK);
+ i.holder().address = p.getAddress().holder().address;
+ return p.getPort();
+ }
+
+ protected synchronized int peekData(DatagramPacket p) throws IOException {
+ doRecv(p, MSG_PEEK);
+ return p.getPort();
+ }
+
+ protected synchronized void receive0(DatagramPacket p) throws IOException {
+ doRecv(p, 0);
+ }
+
+ private void doRecv(DatagramPacket p, int flags) throws IOException {
+ if (isClosed()) {
+ throw new SocketException("Socket closed");
+ }
+
+ if (timeout != 0) {
+ IoBridge.poll(fd, POLLIN | POLLERR, timeout);
+ }
+
+ IoBridge.recvfrom(false, fd, p.getData(), p.getOffset(), p.bufLength, flags, p,
+ connected);
+ }
+
+ protected void setTimeToLive(int ttl) throws IOException {
+ IoBridge.setSocketOption(fd, JAVA_IP_MULTICAST_TTL, ttl);
+ }
+
+ protected int getTimeToLive() throws IOException {
+ return (Integer) IoBridge.getSocketOption(fd, JAVA_IP_MULTICAST_TTL);
+ }
+
+ protected void setTTL(byte ttl) throws IOException {
+ setTimeToLive((int) ttl & 0xff);
+ }
+
+ protected byte getTTL() throws IOException {
+ return (byte) getTimeToLive();
+ }
+
+ private static StructGroupReq makeGroupReq(InetAddress gr_group,
+ NetworkInterface networkInterface) {
+ int gr_interface = (networkInterface != null) ? networkInterface.getIndex() : 0;
+ return new StructGroupReq(gr_interface, gr_group);
+ }
+
+ protected void join(InetAddress inetaddr, NetworkInterface netIf) throws IOException {
+ if (isClosed()) {
+ throw new SocketException("Socket closed");
+ }
+
+ IoBridge.setSocketOption(fd, JAVA_MCAST_JOIN_GROUP, makeGroupReq(inetaddr, netIf));
+ }
+
+ protected void leave(InetAddress inetaddr, NetworkInterface netIf)
+ throws IOException {
+ if (isClosed()) {
+ throw new SocketException("Socket closed");
+ }
+
+ IoBridge.setSocketOption(fd, JAVA_MCAST_LEAVE_GROUP, makeGroupReq(inetaddr, netIf));
+ }
+
+ protected void datagramSocketCreate() throws SocketException {
+ fd = IoBridge.socket(AF_INET6, SOCK_DGRAM, 0);
+ IoBridge.setSocketOption(fd, SO_BROADCAST, true);
+
+ try {
+ Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_MULTICAST_ALL, 0);
+ } catch (ErrnoException errnoException) {
+ throw errnoException.rethrowAsSocketException();
+ }
+ }
+
+ protected void datagramSocketClose() {
+ try {
+ IoBridge.closeAndSignalBlockedThreads(fd);
+ } catch (IOException ignored) { }
+ }
+
+ protected void socketSetOption0(int opt, Object val) throws SocketException {
+ if (isClosed()) {
+ throw new SocketException("Socket closed");
+ }
+
+ IoBridge.setSocketOption(fd, opt, val);
+ }
+
+ protected Object socketGetOption(int opt) throws SocketException {
+ if (isClosed()) {
+ throw new SocketException("Socket closed");
+ }
+
+ return IoBridge.getSocketOption(fd, opt);
+ }
+
+ protected void connect0(InetAddress address, int port) throws SocketException {
+ if (isClosed()) {
+ throw new SocketException("Socket closed");
+ }
+
+ IoBridge.connect(fd, address, port);
+ }
+
+ protected void disconnect0(int family) {
+ if (isClosed()) {
+ return;
+ }
+
+ InetAddress inetAddressUnspec = new InetAddress();
+ inetAddressUnspec.holder().family = AF_UNSPEC;
+
+ try {
+ IoBridge.connect(fd, inetAddressUnspec, 0);
+ } catch (SocketException ignored) { }
+ }
+ // END Android-changed: Rewrote on top of Libcore.io.
+
+ // Android-removed: JNI has been removed
+ // /**
+ // * Perform class load-time initializations.
+ // */
+ // private native static void init();
+}
diff --git a/java/net/PlainSocketImpl.java b/java/net/PlainSocketImpl.java
new file mode 100644
index 0000000..89ee53e
--- /dev/null
+++ b/java/net/PlainSocketImpl.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.net;
+
+import android.system.ErrnoException;
+
+import java.io.IOException;
+import java.io.FileDescriptor;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
+import libcore.io.AsynchronousCloseMonitor;
+import libcore.io.IoBridge;
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+
+import jdk.net.*;
+
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.EAGAIN;
+import static android.system.OsConstants.EBADF;
+import static android.system.OsConstants.EINVAL;
+import static android.system.OsConstants.MSG_OOB;
+import static android.system.OsConstants.POLLERR;
+import static android.system.OsConstants.POLLIN;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_STREAM;
+import static android.system.OsConstants.SHUT_RDWR;
+import static sun.net.ExtendedOptionsImpl.*;
+
+// Android-changed: Rewritten to use android.system POSIX calls and assume AF_INET6.
+/*
+ * On Unix systems we simply delegate to native methods.
+ *
+ * @author Chris Hegarty
+ */
+
+class PlainSocketImpl extends AbstractPlainSocketImpl
+{
+ // Android-removed: Android doesn't need to call native initProto.
+ /*
+ static {
+ initProto();
+ }
+ */
+
+ /**
+ * Constructs an empty instance.
+ */
+ PlainSocketImpl() {
+ // Android-changed: Let PlainSocketImpl construct its own FileDescriptor.
+ this.fd = new FileDescriptor();
+ }
+
+ /**
+ * Constructs an instance with the given file descriptor.
+ */
+ // Android-removed: Let PlainSocketImpl construct its own FileDescriptor.
+ /*
+ PlainSocketImpl(FileDescriptor fd) {
+ this.fd = fd;
+ }
+ */
+
+ protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
+ if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) {
+ super.setOption(name, value);
+ } else {
+ if (isClosedOrPending()) {
+ throw new SocketException("Socket closed");
+ }
+ checkSetOptionPermission(name);
+ checkValueType(value, SocketFlow.class);
+ setFlowOption(getFileDescriptor(), (SocketFlow)value);
+ }
+ }
+
+ protected <T> T getOption(SocketOption<T> name) throws IOException {
+ if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) {
+ return super.getOption(name);
+ }
+ if (isClosedOrPending()) {
+ throw new SocketException("Socket closed");
+ }
+ checkGetOptionPermission(name);
+ SocketFlow flow = SocketFlow.create();
+ getFlowOption(getFileDescriptor(), flow);
+ return (T)flow;
+ }
+
+ // BEGIN Android-changed: Rewrote on top of Libcore.io.
+ protected void socketSetOption(int opt, Object val) throws SocketException {
+ try {
+ socketSetOption0(opt, val);
+ } catch (SocketException se) {
+ if (socket == null || !socket.isConnected())
+ throw se;
+ }
+ }
+
+ void socketCreate(boolean isStream) throws IOException {
+ // The fd object must not change after calling bind, because we rely on this undocumented
+ // behaviour. See libcore.java.net.SocketTest#testFileDescriptorStaysSame.
+ fd.setInt$(IoBridge.socket(AF_INET6, isStream ? SOCK_STREAM : SOCK_DGRAM, 0).getInt$());
+ IoUtils.setFdOwner(fd, this);
+
+ if (serverSocket != null) {
+ IoUtils.setBlocking(fd, false);
+ IoBridge.setSocketOption(fd, SO_REUSEADDR, true);
+ }
+ }
+
+ void socketConnect(InetAddress address, int port, int timeout) throws IOException {
+ if (fd == null || !fd.valid()) {
+ throw new SocketException("Socket closed");
+ }
+
+ IoBridge.connect(fd, address, port, timeout);
+
+ this.address = address;
+ this.port = port;
+
+ if (localport == 0) {
+ // If socket is pending close, fd becomes an AF_UNIX socket and calling
+ // getLocalInetSocketAddress will fail.
+ // http://b/34645743
+ if (!isClosedOrPending()) {
+ localport = IoBridge.getLocalInetSocketAddress(fd).getPort();
+ }
+ }
+ }
+
+ void socketBind(InetAddress address, int port) throws IOException {
+ if (fd == null || !fd.valid()) {
+ throw new SocketException("Socket closed");
+ }
+
+ IoBridge.bind(fd, address, port);
+
+ this.address = address;
+ if (port == 0) {
+ // Now that we're a connected socket, let's extract the port number that the system
+ // chose for us and store it in the Socket object.
+ localport = IoBridge.getLocalInetSocketAddress(fd).getPort();
+ } else {
+ localport = port;
+ }
+ }
+
+ void socketListen(int count) throws IOException {
+ if (fd == null || !fd.valid()) {
+ throw new SocketException("Socket closed");
+ }
+
+ try {
+ Libcore.os.listen(fd, count);
+ } catch (ErrnoException errnoException) {
+ throw errnoException.rethrowAsSocketException();
+ }
+ }
+
+ void socketAccept(SocketImpl s) throws IOException {
+ if (fd == null || !fd.valid()) {
+ throw new SocketException("Socket closed");
+ }
+
+ // poll() with a timeout of 0 means "poll for zero millis", but a Socket timeout == 0 means
+ // "wait forever". When timeout == 0 we pass -1 to poll.
+ if (timeout <= 0) {
+ IoBridge.poll(fd, POLLIN | POLLERR, -1);
+ } else {
+ IoBridge.poll(fd, POLLIN | POLLERR, timeout);
+ }
+
+ InetSocketAddress peerAddress = new InetSocketAddress();
+ try {
+ FileDescriptor newfd = Libcore.os.accept(fd, peerAddress);
+
+ s.fd.setInt$(newfd.getInt$());
+ IoUtils.setFdOwner(s.fd, s);
+ s.address = peerAddress.getAddress();
+ s.port = peerAddress.getPort();
+ } catch (ErrnoException errnoException) {
+ if (errnoException.errno == EAGAIN) {
+ SocketTimeoutException e = new SocketTimeoutException();
+ e.initCause(errnoException);
+ throw e;
+ } else if (errnoException.errno == EINVAL || errnoException.errno == EBADF) {
+ throw new SocketException("Socket closed");
+ }
+ errnoException.rethrowAsSocketException();
+ }
+
+ s.localport = IoBridge.getLocalInetSocketAddress(s.fd).getPort();
+ }
+
+ int socketAvailable() throws IOException {
+ return IoBridge.available(fd);
+ }
+
+ void socketClose0(boolean useDeferredClose) throws IOException {
+ if (fd == null || !fd.valid()) {
+ throw new SocketException("socket already closed");
+ }
+
+ FileDescriptor markerFD = null;
+ if (useDeferredClose) {
+ markerFD = getMarkerFD();
+ }
+
+ if (useDeferredClose && markerFD != null) {
+ try {
+ Libcore.os.dup2(markerFD, fd.getInt$());
+ Libcore.os.close(markerFD);
+
+ // This effectively closes the socket, needs to signal threads that blocks on this
+ // file descriptor.
+ AsynchronousCloseMonitor.signalBlockedThreads(fd);
+ } catch (ErrnoException errnoException) {
+ // close should not throw
+ }
+ } else {
+ // If requested or a markerFD cannot be created, a non-deferred close is performed
+ // instead.
+ IoBridge.closeAndSignalBlockedThreads(fd);
+ }
+ }
+
+ /*
+ * Create the marker file descriptor by establishing a loopback connection which we shutdown but
+ * do not close the fd. The result is an fd that can be used for read/write.
+ *
+ * The purpose is to keep hold of the raw fd handle until we are sure it is not used in any
+ * thread. Otherwise if we close the file descriptor directly, the system might reuse the raw fd
+ * number and threads holding old fd value might behave incorrectly.
+ */
+ private FileDescriptor getMarkerFD() throws SocketException {
+ FileDescriptor fd1 = new FileDescriptor();
+ FileDescriptor fd2 = new FileDescriptor();
+ try {
+ Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd1, fd2);
+
+ // Shutdown fd1, any reads to this fd will get EOF; any writes will get an error.
+ Libcore.os.shutdown(fd1, SHUT_RDWR);
+ Libcore.os.close(fd2);
+ } catch (ErrnoException errnoException) {
+ // We might have reached the maximum file descriptor number and socketpair(2) would
+ // fail. In this case, return null and let caller to fall back to an alternative method
+ // that does not allocate more file descriptors.
+ return null;
+ }
+ return fd1;
+ }
+
+ void socketShutdown(int howto) throws IOException {
+ try {
+ Libcore.os.shutdown(fd, howto);
+ } catch (ErrnoException errnoException) {
+ throw errnoException.rethrowAsIOException();
+ }
+ }
+
+ void socketSetOption0(int cmd, Object value) throws SocketException {
+ // OpenJDK does not set SO_TIMEOUT on Linux.
+ if (cmd == SO_TIMEOUT) {
+ return;
+ }
+
+ IoBridge.setSocketOption(fd, cmd, value);
+ }
+
+ Object socketGetOption(int opt) throws SocketException {
+ return IoBridge.getSocketOption(fd, opt);
+ }
+
+ void socketSendUrgentData(int data) throws IOException {
+ if (fd == null || !fd.valid()) {
+ throw new SocketException("Socket closed");
+ }
+
+ try {
+ byte[] buffer = new byte[] { (byte) data };
+ Libcore.os.sendto(fd, buffer, 0, 1, MSG_OOB, null, 0);
+ } catch (ErrnoException errnoException) {
+ throw errnoException.rethrowAsSocketException();
+ }
+ }
+ // END Android-changed: Rewrote on top of Libcore.io.
+
+}
diff --git a/java/net/PortUnreachableException.java b/java/net/PortUnreachableException.java
new file mode 100644
index 0000000..3e7558b
--- /dev/null
+++ b/java/net/PortUnreachableException.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * Signals that an ICMP Port Unreachable message has been
+ * received on a connected datagram.
+ *
+ * @since 1.4
+ */
+
+public class PortUnreachableException extends SocketException {
+ private static final long serialVersionUID = 8462541992376507323L;
+
+ /**
+ * Constructs a new {@code PortUnreachableException} with a
+ * detail message.
+ * @param msg the detail message
+ */
+ public PortUnreachableException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Construct a new {@code PortUnreachableException} with no
+ * detailed message.
+ */
+ public PortUnreachableException() {}
+
+ // Android-added: PortUnreachableException ctor used by IoBridge.
+ /** @hide */
+ public PortUnreachableException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
diff --git a/java/net/ProtocolException.java b/java/net/ProtocolException.java
new file mode 100644
index 0000000..5944282
--- /dev/null
+++ b/java/net/ProtocolException.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+
+/**
+ * Thrown to indicate that there is an error in the underlying
+ * protocol, such as a TCP error.
+ *
+ * @author Chris Warth
+ * @since JDK1.0
+ */
+public
+class ProtocolException extends IOException {
+ private static final long serialVersionUID = -6098449442062388080L;
+
+ /**
+ * Constructs a new {@code ProtocolException} with the
+ * specified detail message.
+ *
+ * @param host the detail message.
+ */
+ public ProtocolException(String host) {
+ super(host);
+ }
+
+ /**
+ * Constructs a new {@code ProtocolException} with no detail message.
+ */
+ public ProtocolException() {
+ }
+}
diff --git a/java/net/ProtocolFamily.java b/java/net/ProtocolFamily.java
new file mode 100644
index 0000000..5d02326
--- /dev/null
+++ b/java/net/ProtocolFamily.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * Represents a family of communication protocols.
+ *
+ * @since 1.7
+ */
+
+public interface ProtocolFamily {
+ /**
+ * Returns the name of the protocol family.
+ *
+ * @return the name of the protocol family
+ */
+ String name();
+}
diff --git a/java/net/Proxy.java b/java/net/Proxy.java
new file mode 100644
index 0000000..8ba020e
--- /dev/null
+++ b/java/net/Proxy.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * This class represents a proxy setting, typically a type (http, socks) and
+ * a socket address.
+ * A {@code Proxy} is an immutable object.
+ *
+ * @see java.net.ProxySelector
+ * @author Yingxian Wang
+ * @author Jean-Christophe Collet
+ * @since 1.5
+ */
+public class Proxy {
+
+ /**
+ * Represents the proxy type.
+ *
+ * @since 1.5
+ */
+ public enum Type {
+ /**
+ * Represents a direct connection, or the absence of a proxy.
+ */
+ DIRECT,
+ /**
+ * Represents proxy for high level protocols such as HTTP or FTP.
+ */
+ HTTP,
+ /**
+ * Represents a SOCKS (V4 or V5) proxy.
+ */
+ SOCKS
+ };
+
+ private Type type;
+ private SocketAddress sa;
+
+ /**
+ * A proxy setting that represents a {@code DIRECT} connection,
+ * basically telling the protocol handler not to use any proxying.
+ * Used, for instance, to create sockets bypassing any other global
+ * proxy settings (like SOCKS):
+ * <P>
+ * {@code Socket s = new Socket(Proxy.NO_PROXY);}
+ *
+ */
+ public final static Proxy NO_PROXY = new Proxy();
+
+ // Creates the proxy that represents a {@code DIRECT} connection.
+ private Proxy() {
+ type = Type.DIRECT;
+ sa = null;
+ }
+
+ /**
+ * Creates an entry representing a PROXY connection.
+ * Certain combinations are illegal. For instance, for types Http, and
+ * Socks, a SocketAddress <b>must</b> be provided.
+ * <P>
+ * Use the {@code Proxy.NO_PROXY} constant
+ * for representing a direct connection.
+ *
+ * @param type the {@code Type} of the proxy
+ * @param sa the {@code SocketAddress} for that proxy
+ * @throws IllegalArgumentException when the type and the address are
+ * incompatible
+ */
+ public Proxy(Type type, SocketAddress sa) {
+ if ((type == Type.DIRECT) || !(sa instanceof InetSocketAddress))
+ throw new IllegalArgumentException("type " + type + " is not compatible with address " + sa);
+ this.type = type;
+ this.sa = sa;
+ }
+
+ /**
+ * Returns the proxy type.
+ *
+ * @return a Type representing the proxy type
+ */
+ public Type type() {
+ return type;
+ }
+
+ /**
+ * Returns the socket address of the proxy, or
+ * {@code null} if its a direct connection.
+ *
+ * @return a {@code SocketAddress} representing the socket end
+ * point of the proxy
+ */
+ public SocketAddress address() {
+ return sa;
+ }
+
+ /**
+ * Constructs a string representation of this Proxy.
+ * This String is constructed by calling toString() on its type
+ * and concatenating " @ " and the toString() result from its address
+ * if its type is not {@code DIRECT}.
+ *
+ * @return a string representation of this object.
+ */
+ public String toString() {
+ if (type() == Type.DIRECT)
+ return "DIRECT";
+ return type() + " @ " + address();
+ }
+
+ /**
+ * Compares this object against the specified object.
+ * The result is {@code true} if and only if the argument is
+ * not {@code null} and it represents the same proxy as
+ * this object.
+ * <p>
+ * Two instances of {@code Proxy} represent the same
+ * address if both the SocketAddresses and type are equal.
+ *
+ * @param obj the object to compare against.
+ * @return {@code true} if the objects are the same;
+ * {@code false} otherwise.
+ * @see java.net.InetSocketAddress#equals(java.lang.Object)
+ */
+ public final boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof Proxy))
+ return false;
+ Proxy p = (Proxy) obj;
+ if (p.type() == type()) {
+ if (address() == null) {
+ return (p.address() == null);
+ } else
+ return address().equals(p.address());
+ }
+ return false;
+ }
+
+ /**
+ * Returns a hashcode for this Proxy.
+ *
+ * @return a hash code value for this Proxy.
+ */
+ public final int hashCode() {
+ if (address() == null)
+ return type().hashCode();
+ return type().hashCode() + address().hashCode();
+ }
+}
diff --git a/java/net/ProxySelector.java b/java/net/ProxySelector.java
new file mode 100644
index 0000000..d6bb536
--- /dev/null
+++ b/java/net/ProxySelector.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+import java.util.List;
+import sun.security.util.SecurityConstants;
+
+/**
+ * Selects the proxy server to use, if any, when connecting to the
+ * network resource referenced by a URL. A proxy selector is a
+ * concrete sub-class of this class and is registered by invoking the
+ * {@link java.net.ProxySelector#setDefault setDefault} method. The
+ * currently registered proxy selector can be retrieved by calling
+ * {@link java.net.ProxySelector#getDefault getDefault} method.
+ *
+ * <p> When a proxy selector is registered, for instance, a subclass
+ * of URLConnection class should call the {@link #select select}
+ * method for each URL request so that the proxy selector can decide
+ * if a direct, or proxied connection should be used. The {@link
+ * #select select} method returns an iterator over a collection with
+ * the preferred connection approach.
+ *
+ * <p> If a connection cannot be established to a proxy (PROXY or
+ * SOCKS) servers then the caller should call the proxy selector's
+ * {@link #connectFailed connectFailed} method to notify the proxy
+ * selector that the proxy server is unavailable. </p>
+ *
+ * <P>The default proxy selector does enforce a
+ * <a href="doc-files/net-properties.html#Proxies">set of System Properties</a>
+ * related to proxy settings.</P>
+ *
+ * @author Yingxian Wang
+ * @author Jean-Christophe Collet
+ * @since 1.5
+ */
+public abstract class ProxySelector {
+ /**
+ * The system wide proxy selector that selects the proxy server to
+ * use, if any, when connecting to a remote object referenced by
+ * an URL.
+ *
+ * @see #setDefault(ProxySelector)
+ */
+ private static ProxySelector theProxySelector;
+
+ static {
+ try {
+ Class<?> c = Class.forName("sun.net.spi.DefaultProxySelector");
+ if (c != null && ProxySelector.class.isAssignableFrom(c)) {
+ theProxySelector = (ProxySelector) c.newInstance();
+ }
+ } catch (Exception e) {
+ theProxySelector = null;
+ }
+ }
+
+ /**
+ * Gets the system-wide proxy selector.
+ *
+ * @throws SecurityException
+ * If a security manager has been installed and it denies
+ * {@link NetPermission}{@code ("getProxySelector")}
+ * @see #setDefault(ProxySelector)
+ * @return the system-wide {@code ProxySelector}
+ * @since 1.5
+ */
+ public static ProxySelector getDefault() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(SecurityConstants.GET_PROXYSELECTOR_PERMISSION);
+ }
+ return theProxySelector;
+ }
+
+ /**
+ * Sets (or unsets) the system-wide proxy selector.
+ *
+ * Note: non-standard protocol handlers may ignore this setting.
+ *
+ * @param ps The HTTP proxy selector, or
+ * {@code null} to unset the proxy selector.
+ *
+ * @throws SecurityException
+ * If a security manager has been installed and it denies
+ * {@link NetPermission}{@code ("setProxySelector")}
+ *
+ * @see #getDefault()
+ * @since 1.5
+ */
+ public static void setDefault(ProxySelector ps) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(SecurityConstants.SET_PROXYSELECTOR_PERMISSION);
+ }
+ theProxySelector = ps;
+ }
+
+ /**
+ * Selects all the applicable proxies based on the protocol to
+ * access the resource with and a destination address to access
+ * the resource at.
+ * The format of the URI is defined as follow:
+ * <UL>
+ * <LI>http URI for http connections</LI>
+ * <LI>https URI for https connections
+ * <LI>{@code socket://host:port}<br>
+ * for tcp client sockets connections</LI>
+ * </UL>
+ *
+ * @param uri
+ * The URI that a connection is required to
+ *
+ * @return a List of Proxies. Each element in the
+ * the List is of type
+ * {@link java.net.Proxy Proxy};
+ * when no proxy is available, the list will
+ * contain one element of type
+ * {@link java.net.Proxy Proxy}
+ * that represents a direct connection.
+ * @throws IllegalArgumentException if the argument is null
+ */
+ public abstract List<Proxy> select(URI uri);
+
+ /**
+ * Called to indicate that a connection could not be established
+ * to a proxy/socks server. An implementation of this method can
+ * temporarily remove the proxies or reorder the sequence of
+ * proxies returned by {@link #select(URI)}, using the address
+ * and the IOException caught when trying to connect.
+ *
+ * @param uri
+ * The URI that the proxy at sa failed to serve.
+ * @param sa
+ * The socket address of the proxy/SOCKS server
+ *
+ * @param ioe
+ * The I/O exception thrown when the connect failed.
+ * @throws IllegalArgumentException if either argument is null
+ */
+ public abstract void connectFailed(URI uri, SocketAddress sa, IOException ioe);
+}
diff --git a/java/net/ResponseCache.java b/java/net/ResponseCache.java
new file mode 100644
index 0000000..2dfaf4a
--- /dev/null
+++ b/java/net/ResponseCache.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.List;
+import sun.security.util.SecurityConstants;
+
+/**
+ * Represents implementations of URLConnection caches. An instance of
+ * such a class can be registered with the system by doing
+ * ResponseCache.setDefault(ResponseCache), and the system will call
+ * this object in order to:
+ *
+ * <ul><li>store resource data which has been retrieved from an
+ * external source into the cache</li>
+ * <li>try to fetch a requested resource that may have been
+ * stored in the cache</li>
+ * </ul>
+ *
+ * The ResponseCache implementation decides which resources
+ * should be cached, and for how long they should be cached. If a
+ * request resource cannot be retrieved from the cache, then the
+ * protocol handlers will fetch the resource from its original
+ * location.
+ *
+ * The settings for URLConnection#useCaches controls whether the
+ * protocol is allowed to use a cached response.
+ *
+ * For more information on HTTP caching, see <a
+ * href="http://www.ietf.org/rfc/rfc2616.txt"><i>RFC 2616: Hypertext
+ * Transfer Protocol -- HTTP/1.1</i></a>
+ *
+ * @author Yingxian Wang
+ * @since 1.5
+ */
+public abstract class ResponseCache {
+
+ /**
+ * The system wide cache that provides access to a url
+ * caching mechanism.
+ *
+ * @see #setDefault(ResponseCache)
+ * @see #getDefault()
+ */
+ private static ResponseCache theResponseCache;
+
+ /**
+ * Gets the system-wide response cache.
+ *
+ * @throws SecurityException
+ * If a security manager has been installed and it denies
+ * {@link NetPermission}{@code ("getResponseCache")}
+ *
+ * @see #setDefault(ResponseCache)
+ * @return the system-wide {@code ResponseCache}
+ * @since 1.5
+ */
+ public synchronized static ResponseCache getDefault() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(SecurityConstants.GET_RESPONSECACHE_PERMISSION);
+ }
+ return theResponseCache;
+ }
+
+ /**
+ * Sets (or unsets) the system-wide cache.
+ *
+ * Note: non-standard procotol handlers may ignore this setting.
+ *
+ * @param responseCache The response cache, or
+ * {@code null} to unset the cache.
+ *
+ * @throws SecurityException
+ * If a security manager has been installed and it denies
+ * {@link NetPermission}{@code ("setResponseCache")}
+ *
+ * @see #getDefault()
+ * @since 1.5
+ */
+ public synchronized static void setDefault(ResponseCache responseCache) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(SecurityConstants.SET_RESPONSECACHE_PERMISSION);
+ }
+ theResponseCache = responseCache;
+ }
+
+ /**
+ * Retrieve the cached response based on the requesting uri,
+ * request method and request headers. Typically this method is
+ * called by the protocol handler before it sends out the request
+ * to get the network resource. If a cached response is returned,
+ * that resource is used instead.
+ *
+ * @param uri a {@code URI} used to reference the requested
+ * network resource
+ * @param rqstMethod a {@code String} representing the request
+ * method
+ * @param rqstHeaders - a Map from request header
+ * field names to lists of field values representing
+ * the current request headers
+ * @return a {@code CacheResponse} instance if available
+ * from cache, or null otherwise
+ * @throws IOException if an I/O error occurs
+ * @throws IllegalArgumentException if any one of the arguments is null
+ *
+ * @see java.net.URLConnection#setUseCaches(boolean)
+ * @see java.net.URLConnection#getUseCaches()
+ * @see java.net.URLConnection#setDefaultUseCaches(boolean)
+ * @see java.net.URLConnection#getDefaultUseCaches()
+ */
+ public abstract CacheResponse
+ get(URI uri, String rqstMethod, Map<String, List<String>> rqstHeaders)
+ throws IOException;
+
+ /**
+ * The protocol handler calls this method after a resource has
+ * been retrieved, and the ResponseCache must decide whether or
+ * not to store the resource in its cache. If the resource is to
+ * be cached, then put() must return a CacheRequest object which
+ * contains an OutputStream that the protocol handler will
+ * use to write the resource into the cache. If the resource is
+ * not to be cached, then put must return null.
+ *
+ * @param uri a {@code URI} used to reference the requested
+ * network resource
+ * @param conn - a URLConnection instance that is used to fetch
+ * the response to be cached
+ * @return a {@code CacheRequest} for recording the
+ * response to be cached. Null return indicates that
+ * the caller does not intend to cache the response.
+ * @throws IOException if an I/O error occurs
+ * @throws IllegalArgumentException if any one of the arguments is
+ * null
+ */
+ public abstract CacheRequest put(URI uri, URLConnection conn) throws IOException;
+}
diff --git a/java/net/SecureCacheResponse.java b/java/net/SecureCacheResponse.java
new file mode 100644
index 0000000..64fd414
--- /dev/null
+++ b/java/net/SecureCacheResponse.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.security.cert.Certificate;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import java.security.Principal;
+import java.util.List;
+
+/**
+ * Represents a cache response originally retrieved through secure
+ * means, such as TLS.
+ *
+ * @since 1.5
+ */
+public abstract class SecureCacheResponse extends CacheResponse {
+ /**
+ * Returns the cipher suite in use on the original connection that
+ * retrieved the network resource.
+ *
+ * @return a string representing the cipher suite
+ */
+ public abstract String getCipherSuite();
+
+ /**
+ * Returns the certificate chain that were sent to the server during
+ * handshaking of the original connection that retrieved the
+ * network resource. Note: This method is useful only
+ * when using certificate-based cipher suites.
+ *
+ * @return an immutable List of Certificate representing the
+ * certificate chain that was sent to the server. If no
+ * certificate chain was sent, null will be returned.
+ * @see #getLocalPrincipal()
+ */
+ public abstract List<Certificate> getLocalCertificateChain();
+
+ /**
+ * Returns the server's certificate chain, which was established as
+ * part of defining the session in the original connection that
+ * retrieved the network resource, from cache. Note: This method
+ * can be used only when using certificate-based cipher suites;
+ * using it with non-certificate-based cipher suites, such as
+ * Kerberos, will throw an SSLPeerUnverifiedException.
+ *
+ * @return an immutable List of Certificate representing the server's
+ * certificate chain.
+ * @throws SSLPeerUnverifiedException if the peer is not verified.
+ * @see #getPeerPrincipal()
+ */
+ public abstract List<Certificate> getServerCertificateChain()
+ throws SSLPeerUnverifiedException;
+
+ /**
+ * Returns the server's principal which was established as part of
+ * defining the session during the original connection that
+ * retrieved the network resource.
+ *
+ * @return the server's principal. Returns an X500Principal of the
+ * end-entity certiticate for X509-based cipher suites, and
+ * KerberosPrincipal for Kerberos cipher suites.
+ *
+ * @throws SSLPeerUnverifiedException if the peer was not verified.
+ *
+ * @see #getServerCertificateChain()
+ * @see #getLocalPrincipal()
+ */
+ public abstract Principal getPeerPrincipal()
+ throws SSLPeerUnverifiedException;
+
+ /**
+ * Returns the principal that was sent to the server during
+ * handshaking in the original connection that retrieved the
+ * network resource.
+ *
+ * @return the principal sent to the server. Returns an X500Principal
+ * of the end-entity certificate for X509-based cipher suites, and
+ * KerberosPrincipal for Kerberos cipher suites. If no principal was
+ * sent, then null is returned.
+ *
+ * @see #getLocalCertificateChain()
+ * @see #getPeerPrincipal()
+ */
+ public abstract Principal getLocalPrincipal();
+}
diff --git a/java/net/ServerSocket.annotated.java b/java/net/ServerSocket.annotated.java
new file mode 100644
index 0000000..1fcd05f
--- /dev/null
+++ b/java/net/ServerSocket.annotated.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package java.net;
+
+import java.nio.channels.ServerSocketChannel;
+import java.io.IOException;
+
+@SuppressWarnings({"unchecked", "deprecation", "all"})
+public class ServerSocket implements java.io.Closeable {
+
+public ServerSocket() throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public ServerSocket(int port) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public ServerSocket(int port, int backlog) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public ServerSocket(int port, int backlog, java.net.InetAddress bindAddr) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public void bind(java.net.SocketAddress endpoint) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public void bind(java.net.SocketAddress endpoint, int backlog) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public java.net.InetAddress getInetAddress() { throw new RuntimeException("Stub!"); }
+
+public int getLocalPort() { throw new RuntimeException("Stub!"); }
+
+public java.net.SocketAddress getLocalSocketAddress() { throw new RuntimeException("Stub!"); }
+
+public java.net.Socket accept() throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
[email protected]
+public SocketImpl getImpl() throws SocketException { throw new RuntimeException("Stub!"); }
+
+protected final void implAccept(java.net.Socket s) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public void close() throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public java.nio.channels.ServerSocketChannel getChannel() { throw new RuntimeException("Stub!"); }
+
+public boolean isBound() { throw new RuntimeException("Stub!"); }
+
+public boolean isClosed() { throw new RuntimeException("Stub!"); }
+
+public synchronized void setSoTimeout(int timeout) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized int getSoTimeout() throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public void setReuseAddress(boolean on) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public boolean getReuseAddress() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public java.lang.String toString() { throw new RuntimeException("Stub!"); }
+
+public static synchronized void setSocketFactory(java.net.SocketImplFactory fac) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public synchronized void setReceiveBufferSize(int size) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized int getReceiveBufferSize() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { throw new RuntimeException("Stub!"); }
+}
+
diff --git a/java/net/ServerSocket.java b/java/net/ServerSocket.java
new file mode 100644
index 0000000..444a84d
--- /dev/null
+++ b/java/net/ServerSocket.java
@@ -0,0 +1,932 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.nio.channels.ServerSocketChannel;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+
+/**
+ * This class implements server sockets. A server socket waits for
+ * requests to come in over the network. It performs some operation
+ * based on that request, and then possibly returns a result to the requester.
+ * <p>
+ * The actual work of the server socket is performed by an instance
+ * of the {@code SocketImpl} class. An application can
+ * change the socket factory that creates the socket
+ * implementation to configure itself to create sockets
+ * appropriate to the local firewall.
+ *
+ * @author unascribed
+ * @see java.net.SocketImpl
+ * @see java.net.ServerSocket#setSocketFactory(java.net.SocketImplFactory)
+ * @see java.nio.channels.ServerSocketChannel
+ * @since JDK1.0
+ */
+public
+class ServerSocket implements java.io.Closeable {
+ /**
+ * Various states of this socket.
+ */
+ private boolean created = false;
+ private boolean bound = false;
+ private boolean closed = false;
+ private Object closeLock = new Object();
+
+ /**
+ * The implementation of this Socket.
+ */
+ private SocketImpl impl;
+
+ /**
+ * Are we using an older SocketImpl?
+ */
+ private boolean oldImpl = false;
+
+ /**
+ * Package-private constructor to create a ServerSocket associated with
+ * the given SocketImpl.
+ */
+ ServerSocket(SocketImpl impl) {
+ this.impl = impl;
+ impl.setServerSocket(this);
+ }
+
+ /**
+ * Creates an unbound server socket.
+ *
+ * @exception IOException IO error when opening the socket.
+ * @revised 1.4
+ */
+ public ServerSocket() throws IOException {
+ setImpl();
+ }
+
+ /**
+ * Creates a server socket, bound to the specified port. A port number
+ * of {@code 0} means that the port number is automatically
+ * allocated, typically from an ephemeral port range. This port
+ * number can then be retrieved by calling {@link #getLocalPort getLocalPort}.
+ * <p>
+ * The maximum queue length for incoming connection indications (a
+ * request to connect) is set to {@code 50}. If a connection
+ * indication arrives when the queue is full, the connection is refused.
+ * <p>
+ * If the application has specified a server socket factory, that
+ * factory's {@code createSocketImpl} method is called to create
+ * the actual socket implementation. Otherwise a "plain" socket is created.
+ * <p>
+ * If there is a security manager,
+ * its {@code checkListen} method is called
+ * with the {@code port} argument
+ * as its argument to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ *
+ *
+ * @param port the port number, or {@code 0} to use a port
+ * number that is automatically allocated.
+ *
+ * @exception IOException if an I/O error occurs when opening the socket.
+ * @exception SecurityException
+ * if a security manager exists and its {@code checkListen}
+ * method doesn't allow the operation.
+ * @exception IllegalArgumentException if the port parameter is outside
+ * the specified range of valid port values, which is between
+ * 0 and 65535, inclusive.
+ *
+ * @see java.net.SocketImpl
+ * @see java.net.SocketImplFactory#createSocketImpl()
+ * @see java.net.ServerSocket#setSocketFactory(java.net.SocketImplFactory)
+ * @see SecurityManager#checkListen
+ */
+ public ServerSocket(int port) throws IOException {
+ this(port, 50, null);
+ }
+
+ /**
+ * Creates a server socket and binds it to the specified local port
+ * number, with the specified backlog.
+ * A port number of {@code 0} means that the port number is
+ * automatically allocated, typically from an ephemeral port range.
+ * This port number can then be retrieved by calling
+ * {@link #getLocalPort getLocalPort}.
+ * <p>
+ * The maximum queue length for incoming connection indications (a
+ * request to connect) is set to the {@code backlog} parameter. If
+ * a connection indication arrives when the queue is full, the
+ * connection is refused.
+ * <p>
+ * If the application has specified a server socket factory, that
+ * factory's {@code createSocketImpl} method is called to create
+ * the actual socket implementation. Otherwise a "plain" socket is created.
+ * <p>
+ * If there is a security manager,
+ * its {@code checkListen} method is called
+ * with the {@code port} argument
+ * as its argument to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ *
+ * The {@code backlog} argument is the requested maximum number of
+ * pending connections on the socket. Its exact semantics are implementation
+ * specific. In particular, an implementation may impose a maximum length
+ * or may choose to ignore the parameter altogther. The value provided
+ * should be greater than {@code 0}. If it is less than or equal to
+ * {@code 0}, then an implementation specific default will be used.
+ * <P>
+ *
+ * @param port the port number, or {@code 0} to use a port
+ * number that is automatically allocated.
+ * @param backlog requested maximum length of the queue of incoming
+ * connections.
+ *
+ * @exception IOException if an I/O error occurs when opening the socket.
+ * @exception SecurityException
+ * if a security manager exists and its {@code checkListen}
+ * method doesn't allow the operation.
+ * @exception IllegalArgumentException if the port parameter is outside
+ * the specified range of valid port values, which is between
+ * 0 and 65535, inclusive.
+ *
+ * @see java.net.SocketImpl
+ * @see java.net.SocketImplFactory#createSocketImpl()
+ * @see java.net.ServerSocket#setSocketFactory(java.net.SocketImplFactory)
+ * @see SecurityManager#checkListen
+ */
+ public ServerSocket(int port, int backlog) throws IOException {
+ this(port, backlog, null);
+ }
+
+ /**
+ * Create a server with the specified port, listen backlog, and
+ * local IP address to bind to. The <i>bindAddr</i> argument
+ * can be used on a multi-homed host for a ServerSocket that
+ * will only accept connect requests to one of its addresses.
+ * If <i>bindAddr</i> is null, it will default accepting
+ * connections on any/all local addresses.
+ * The port must be between 0 and 65535, inclusive.
+ * A port number of {@code 0} means that the port number is
+ * automatically allocated, typically from an ephemeral port range.
+ * This port number can then be retrieved by calling
+ * {@link #getLocalPort getLocalPort}.
+ *
+ * <P>If there is a security manager, this method
+ * calls its {@code checkListen} method
+ * with the {@code port} argument
+ * as its argument to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ *
+ * The {@code backlog} argument is the requested maximum number of
+ * pending connections on the socket. Its exact semantics are implementation
+ * specific. In particular, an implementation may impose a maximum length
+ * or may choose to ignore the parameter altogther. The value provided
+ * should be greater than {@code 0}. If it is less than or equal to
+ * {@code 0}, then an implementation specific default will be used.
+ * <P>
+ * @param port the port number, or {@code 0} to use a port
+ * number that is automatically allocated.
+ * @param backlog requested maximum length of the queue of incoming
+ * connections.
+ * @param bindAddr the local InetAddress the server will bind to
+ *
+ * @throws SecurityException if a security manager exists and
+ * its {@code checkListen} method doesn't allow the operation.
+ *
+ * @throws IOException if an I/O error occurs when opening the socket.
+ * @exception IllegalArgumentException if the port parameter is outside
+ * the specified range of valid port values, which is between
+ * 0 and 65535, inclusive.
+ *
+ * @see SocketOptions
+ * @see SocketImpl
+ * @see SecurityManager#checkListen
+ * @since JDK1.1
+ */
+ public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
+ setImpl();
+ if (port < 0 || port > 0xFFFF)
+ throw new IllegalArgumentException(
+ "Port value out of range: " + port);
+ if (backlog < 1)
+ backlog = 50;
+ try {
+ bind(new InetSocketAddress(bindAddr, port), backlog);
+ } catch(SecurityException e) {
+ close();
+ throw e;
+ } catch(IOException e) {
+ close();
+ throw e;
+ }
+ }
+
+ // Android-changed: Made getImpl() public and @hide, for internal use.
+ /**
+ * Get the {@code SocketImpl} attached to this socket, creating
+ * it if necessary.
+ *
+ * @return the {@code SocketImpl} attached to that ServerSocket.
+ * @throws SocketException if creation fails.
+ * @since 1.4
+ * @hide
+ */
+ public SocketImpl getImpl() throws SocketException {
+ if (!created)
+ createImpl();
+ return impl;
+ }
+
+ private void checkOldImpl() {
+ if (impl == null)
+ return;
+ // SocketImpl.connect() is a protected method, therefore we need to use
+ // getDeclaredMethod, therefore we need permission to access the member
+ try {
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Void>() {
+ public Void run() throws NoSuchMethodException {
+ impl.getClass().getDeclaredMethod("connect",
+ SocketAddress.class,
+ int.class);
+ return null;
+ }
+ });
+ } catch (java.security.PrivilegedActionException e) {
+ oldImpl = true;
+ }
+ }
+
+ private void setImpl() {
+ if (factory != null) {
+ impl = factory.createSocketImpl();
+ checkOldImpl();
+ } else {
+ // No need to do a checkOldImpl() here, we know it's an up to date
+ // SocketImpl!
+ impl = new SocksSocketImpl();
+ }
+ if (impl != null)
+ impl.setServerSocket(this);
+ }
+
+ /**
+ * Creates the socket implementation.
+ *
+ * @throws IOException if creation fails
+ * @since 1.4
+ */
+ void createImpl() throws SocketException {
+ if (impl == null)
+ setImpl();
+ try {
+ impl.create(true);
+ created = true;
+ } catch (IOException e) {
+ throw new SocketException(e.getMessage());
+ }
+ }
+
+ /**
+ *
+ * Binds the {@code ServerSocket} to a specific address
+ * (IP address and port number).
+ * <p>
+ * If the address is {@code null}, then the system will pick up
+ * an ephemeral port and a valid local address to bind the socket.
+ * <p>
+ * @param endpoint The IP address and port number to bind to.
+ * @throws IOException if the bind operation fails, or if the socket
+ * is already bound.
+ * @throws SecurityException if a {@code SecurityManager} is present and
+ * its {@code checkListen} method doesn't allow the operation.
+ * @throws IllegalArgumentException if endpoint is a
+ * SocketAddress subclass not supported by this socket
+ * @since 1.4
+ */
+ public void bind(SocketAddress endpoint) throws IOException {
+ bind(endpoint, 50);
+ }
+
+ /**
+ *
+ * Binds the {@code ServerSocket} to a specific address
+ * (IP address and port number).
+ * <p>
+ * If the address is {@code null}, then the system will pick up
+ * an ephemeral port and a valid local address to bind the socket.
+ * <P>
+ * The {@code backlog} argument is the requested maximum number of
+ * pending connections on the socket. Its exact semantics are implementation
+ * specific. In particular, an implementation may impose a maximum length
+ * or may choose to ignore the parameter altogther. The value provided
+ * should be greater than {@code 0}. If it is less than or equal to
+ * {@code 0}, then an implementation specific default will be used.
+ * @param endpoint The IP address and port number to bind to.
+ * @param backlog requested maximum length of the queue of
+ * incoming connections.
+ * @throws IOException if the bind operation fails, or if the socket
+ * is already bound.
+ * @throws SecurityException if a {@code SecurityManager} is present and
+ * its {@code checkListen} method doesn't allow the operation.
+ * @throws IllegalArgumentException if endpoint is a
+ * SocketAddress subclass not supported by this socket
+ * @since 1.4
+ */
+ public void bind(SocketAddress endpoint, int backlog) throws IOException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ if (!oldImpl && isBound())
+ throw new SocketException("Already bound");
+ if (endpoint == null)
+ endpoint = new InetSocketAddress(0);
+ if (!(endpoint instanceof InetSocketAddress))
+ throw new IllegalArgumentException("Unsupported address type");
+ InetSocketAddress epoint = (InetSocketAddress) endpoint;
+ if (epoint.isUnresolved())
+ throw new SocketException("Unresolved address");
+ if (backlog < 1)
+ backlog = 50;
+ try {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null)
+ security.checkListen(epoint.getPort());
+ getImpl().bind(epoint.getAddress(), epoint.getPort());
+ getImpl().listen(backlog);
+ bound = true;
+ } catch(SecurityException e) {
+ bound = false;
+ throw e;
+ } catch(IOException e) {
+ bound = false;
+ throw e;
+ }
+ }
+
+ /**
+ * Returns the local address of this server socket.
+ * <p>
+ * If the socket was bound prior to being {@link #close closed},
+ * then this method will continue to return the local address
+ * after the socket is closed.
+ * <p>
+ * If there is a security manager set, its {@code checkConnect} method is
+ * called with the local address and {@code -1} as its arguments to see
+ * if the operation is allowed. If the operation is not allowed,
+ * the {@link InetAddress#getLoopbackAddress loopback} address is returned.
+ *
+ * @return the address to which this socket is bound,
+ * or the loopback address if denied by the security manager,
+ * or {@code null} if the socket is unbound.
+ *
+ * @see SecurityManager#checkConnect
+ */
+ public InetAddress getInetAddress() {
+ if (!isBound())
+ return null;
+ try {
+ InetAddress in = getImpl().getInetAddress();
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkConnect(in.getHostAddress(), -1);
+ return in;
+ } catch (SecurityException e) {
+ return InetAddress.getLoopbackAddress();
+ } catch (SocketException e) {
+ // nothing
+ // If we're bound, the impl has been created
+ // so we shouldn't get here
+ }
+ return null;
+ }
+
+ /**
+ * Returns the port number on which this socket is listening.
+ * <p>
+ * If the socket was bound prior to being {@link #close closed},
+ * then this method will continue to return the port number
+ * after the socket is closed.
+ *
+ * @return the port number to which this socket is listening or
+ * -1 if the socket is not bound yet.
+ */
+ public int getLocalPort() {
+ if (!isBound())
+ return -1;
+ try {
+ return getImpl().getLocalPort();
+ } catch (SocketException e) {
+ // nothing
+ // If we're bound, the impl has been created
+ // so we shouldn't get here
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the address of the endpoint this socket is bound to.
+ * <p>
+ * If the socket was bound prior to being {@link #close closed},
+ * then this method will continue to return the address of the endpoint
+ * after the socket is closed.
+ * <p>
+ * If there is a security manager set, its {@code checkConnect} method is
+ * called with the local address and {@code -1} as its arguments to see
+ * if the operation is allowed. If the operation is not allowed,
+ * a {@code SocketAddress} representing the
+ * {@link InetAddress#getLoopbackAddress loopback} address and the local
+ * port to which the socket is bound is returned.
+ *
+ * @return a {@code SocketAddress} representing the local endpoint of
+ * this socket, or a {@code SocketAddress} representing the
+ * loopback address if denied by the security manager,
+ * or {@code null} if the socket is not bound yet.
+ *
+ * @see #getInetAddress()
+ * @see #getLocalPort()
+ * @see #bind(SocketAddress)
+ * @see SecurityManager#checkConnect
+ * @since 1.4
+ */
+
+ public SocketAddress getLocalSocketAddress() {
+ if (!isBound())
+ return null;
+ return new InetSocketAddress(getInetAddress(), getLocalPort());
+ }
+
+ /**
+ * Listens for a connection to be made to this socket and accepts
+ * it. The method blocks until a connection is made.
+ *
+ * <p>A new Socket {@code s} is created and, if there
+ * is a security manager,
+ * the security manager's {@code checkAccept} method is called
+ * with {@code s.getInetAddress().getHostAddress()} and
+ * {@code s.getPort()}
+ * as its arguments to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ *
+ * @exception IOException if an I/O error occurs when waiting for a
+ * connection.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkAccept} method doesn't allow the operation.
+ * @exception SocketTimeoutException if a timeout was previously set with setSoTimeout and
+ * the timeout has been reached.
+ * @exception java.nio.channels.IllegalBlockingModeException
+ * if this socket has an associated channel, the channel is in
+ * non-blocking mode, and there is no connection ready to be
+ * accepted
+ *
+ * @return the new Socket
+ * @see SecurityManager#checkAccept
+ * @revised 1.4
+ * @spec JSR-51
+ */
+ public Socket accept() throws IOException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ if (!isBound())
+ throw new SocketException("Socket is not bound yet");
+ Socket s = new Socket((SocketImpl) null);
+ implAccept(s);
+ return s;
+ }
+
+ /**
+ * Subclasses of ServerSocket use this method to override accept()
+ * to return their own subclass of socket. So a FooServerSocket
+ * will typically hand this method an <i>empty</i> FooSocket. On
+ * return from implAccept the FooSocket will be connected to a client.
+ *
+ * @param s the Socket
+ * @throws java.nio.channels.IllegalBlockingModeException
+ * if this socket has an associated channel,
+ * and the channel is in non-blocking mode
+ * @throws IOException if an I/O error occurs when waiting
+ * for a connection.
+ * @since JDK1.1
+ * @revised 1.4
+ * @spec JSR-51
+ */
+ protected final void implAccept(Socket s) throws IOException {
+ SocketImpl si = null;
+ try {
+ if (s.impl == null)
+ s.setImpl();
+ else {
+ s.impl.reset();
+ }
+ si = s.impl;
+ s.impl = null;
+ si.address = new InetAddress();
+ si.fd = new FileDescriptor();
+ getImpl().accept(si);
+
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkAccept(si.getInetAddress().getHostAddress(),
+ si.getPort());
+ }
+ } catch (IOException e) {
+ if (si != null)
+ si.reset();
+ s.impl = si;
+ throw e;
+ } catch (SecurityException e) {
+ if (si != null)
+ si.reset();
+ s.impl = si;
+ throw e;
+ }
+ s.impl = si;
+ s.postAccept();
+ }
+
+ /**
+ * Closes this socket.
+ *
+ * Any thread currently blocked in {@link #accept()} will throw
+ * a {@link SocketException}.
+ *
+ * <p> If this socket has an associated channel then the channel is closed
+ * as well.
+ *
+ * @exception IOException if an I/O error occurs when closing the socket.
+ * @revised 1.4
+ * @spec JSR-51
+ */
+ public void close() throws IOException {
+ synchronized(closeLock) {
+ if (isClosed())
+ return;
+ if (created)
+ impl.close();
+ closed = true;
+ }
+ }
+
+ /**
+ * Returns the unique {@link java.nio.channels.ServerSocketChannel} object
+ * associated with this socket, if any.
+ *
+ * <p> A server socket will have a channel if, and only if, the channel
+ * itself was created via the {@link
+ * java.nio.channels.ServerSocketChannel#open ServerSocketChannel.open}
+ * method.
+ *
+ * @return the server-socket channel associated with this socket,
+ * or {@code null} if this socket was not created
+ * for a channel
+ *
+ * @since 1.4
+ * @spec JSR-51
+ */
+ public ServerSocketChannel getChannel() {
+ return null;
+ }
+
+ /**
+ * Returns the binding state of the ServerSocket.
+ *
+ * @return true if the ServerSocket successfully bound to an address
+ * @since 1.4
+ */
+ public boolean isBound() {
+ // Before 1.3 ServerSockets were always bound during creation
+ return bound || oldImpl;
+ }
+
+ /**
+ * Returns the closed state of the ServerSocket.
+ *
+ * @return true if the socket has been closed
+ * @since 1.4
+ */
+ public boolean isClosed() {
+ synchronized(closeLock) {
+ return closed;
+ }
+ }
+
+ /**
+ * Enable/disable {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT} with the
+ * specified timeout, in milliseconds. With this option set to a non-zero
+ * timeout, a call to accept() for this ServerSocket
+ * will block for only this amount of time. If the timeout expires,
+ * a <B>java.net.SocketTimeoutException</B> is raised, though the
+ * ServerSocket is still valid. The option <B>must</B> be enabled
+ * prior to entering the blocking operation to have effect. The
+ * timeout must be {@code > 0}.
+ * A timeout of zero is interpreted as an infinite timeout.
+ * @param timeout the specified timeout, in milliseconds
+ * @exception SocketException if there is an error in
+ * the underlying protocol, such as a TCP error.
+ * @since JDK1.1
+ * @see #getSoTimeout()
+ */
+ public synchronized void setSoTimeout(int timeout) throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ getImpl().setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout));
+ }
+
+ /**
+ * Retrieve setting for {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT}.
+ * 0 returns implies that the option is disabled (i.e., timeout of infinity).
+ * @return the {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT} value
+ * @exception IOException if an I/O error occurs
+ * @since JDK1.1
+ * @see #setSoTimeout(int)
+ */
+ public synchronized int getSoTimeout() throws IOException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ Object o = getImpl().getOption(SocketOptions.SO_TIMEOUT);
+ /* extra type safety */
+ if (o instanceof Integer) {
+ return ((Integer) o).intValue();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Enable/disable the {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR}
+ * socket option.
+ * <p>
+ * When a TCP connection is closed the connection may remain
+ * in a timeout state for a period of time after the connection
+ * is closed (typically known as the {@code TIME_WAIT} state
+ * or {@code 2MSL} wait state).
+ * For applications using a well known socket address or port
+ * it may not be possible to bind a socket to the required
+ * {@code SocketAddress} if there is a connection in the
+ * timeout state involving the socket address or port.
+ * <p>
+ * Enabling {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} prior to
+ * binding the socket using {@link #bind(SocketAddress)} allows the socket
+ * to be bound even though a previous connection is in a timeout state.
+ * <p>
+ * When a {@code ServerSocket} is created the initial setting
+ * of {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is not defined.
+ * Applications can use {@link #getReuseAddress()} to determine the initial
+ * setting of {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR}.
+ * <p>
+ * The behaviour when {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is
+ * enabled or disabled after a socket is bound (See {@link #isBound()})
+ * is not defined.
+ *
+ * @param on whether to enable or disable the socket option
+ * @exception SocketException if an error occurs enabling or
+ * disabling the {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR}
+ * socket option, or the socket is closed.
+ * @since 1.4
+ * @see #getReuseAddress()
+ * @see #bind(SocketAddress)
+ * @see #isBound()
+ * @see #isClosed()
+ */
+ public void setReuseAddress(boolean on) throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on));
+ }
+
+ /**
+ * Tests if {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is enabled.
+ *
+ * @return a {@code boolean} indicating whether or not
+ * {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is enabled.
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ * @since 1.4
+ * @see #setReuseAddress(boolean)
+ */
+ public boolean getReuseAddress() throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ return ((Boolean) (getImpl().getOption(SocketOptions.SO_REUSEADDR))).booleanValue();
+ }
+
+ /**
+ * Returns the implementation address and implementation port of
+ * this socket as a {@code String}.
+ * <p>
+ * If there is a security manager set, its {@code checkConnect} method is
+ * called with the local address and {@code -1} as its arguments to see
+ * if the operation is allowed. If the operation is not allowed,
+ * an {@code InetAddress} representing the
+ * {@link InetAddress#getLoopbackAddress loopback} address is returned as
+ * the implementation address.
+ *
+ * @return a string representation of this socket.
+ */
+ public String toString() {
+ if (!isBound())
+ return "ServerSocket[unbound]";
+ InetAddress in;
+ if (System.getSecurityManager() != null)
+ in = InetAddress.getLoopbackAddress();
+ else
+ in = impl.getInetAddress();
+ return "ServerSocket[addr=" + in +
+ ",localport=" + impl.getLocalPort() + "]";
+ }
+
+ void setBound() {
+ bound = true;
+ }
+
+ void setCreated() {
+ created = true;
+ }
+
+ /**
+ * The factory for all server sockets.
+ */
+ private static SocketImplFactory factory = null;
+
+ /**
+ * Sets the server socket implementation factory for the
+ * application. The factory can be specified only once.
+ * <p>
+ * When an application creates a new server socket, the socket
+ * implementation factory's {@code createSocketImpl} method is
+ * called to create the actual socket implementation.
+ * <p>
+ * Passing {@code null} to the method is a no-op unless the factory
+ * was already set.
+ * <p>
+ * If there is a security manager, this method first calls
+ * the security manager's {@code checkSetFactory} method
+ * to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ *
+ * @param fac the desired factory.
+ * @exception IOException if an I/O error occurs when setting the
+ * socket factory.
+ * @exception SocketException if the factory has already been defined.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkSetFactory} method doesn't allow the operation.
+ * @see java.net.SocketImplFactory#createSocketImpl()
+ * @see SecurityManager#checkSetFactory
+ */
+ public static synchronized void setSocketFactory(SocketImplFactory fac) throws IOException {
+ if (factory != null) {
+ throw new SocketException("factory already defined");
+ }
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkSetFactory();
+ }
+ factory = fac;
+ }
+
+ /**
+ * Sets a default proposed value for the
+ * {@link SocketOptions#SO_RCVBUF SO_RCVBUF} option for sockets
+ * accepted from this {@code ServerSocket}. The value actually set
+ * in the accepted socket must be determined by calling
+ * {@link Socket#getReceiveBufferSize()} after the socket
+ * is returned by {@link #accept()}.
+ * <p>
+ * The value of {@link SocketOptions#SO_RCVBUF SO_RCVBUF} is used both to
+ * set the size of the internal socket receive buffer, and to set the size
+ * of the TCP receive window that is advertized to the remote peer.
+ * <p>
+ * It is possible to change the value subsequently, by calling
+ * {@link Socket#setReceiveBufferSize(int)}. However, if the application
+ * wishes to allow a receive window larger than 64K bytes, as defined by RFC1323
+ * then the proposed value must be set in the ServerSocket <B>before</B>
+ * it is bound to a local address. This implies, that the ServerSocket must be
+ * created with the no-argument constructor, then setReceiveBufferSize() must
+ * be called and lastly the ServerSocket is bound to an address by calling bind().
+ * <p>
+ * Failure to do this will not cause an error, and the buffer size may be set to the
+ * requested value but the TCP receive window in sockets accepted from
+ * this ServerSocket will be no larger than 64K bytes.
+ *
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ *
+ * @param size the size to which to set the receive buffer
+ * size. This value must be greater than 0.
+ *
+ * @exception IllegalArgumentException if the
+ * value is 0 or is negative.
+ *
+ * @since 1.4
+ * @see #getReceiveBufferSize
+ */
+ public synchronized void setReceiveBufferSize (int size) throws SocketException {
+ if (!(size > 0)) {
+ throw new IllegalArgumentException("negative receive size");
+ }
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ getImpl().setOption(SocketOptions.SO_RCVBUF, new Integer(size));
+ }
+
+ /**
+ * Gets the value of the {@link SocketOptions#SO_RCVBUF SO_RCVBUF} option
+ * for this {@code ServerSocket}, that is the proposed buffer size that
+ * will be used for Sockets accepted from this {@code ServerSocket}.
+ *
+ * <p>Note, the value actually set in the accepted socket is determined by
+ * calling {@link Socket#getReceiveBufferSize()}.
+ * @return the value of the {@link SocketOptions#SO_RCVBUF SO_RCVBUF}
+ * option for this {@code Socket}.
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ * @see #setReceiveBufferSize(int)
+ * @since 1.4
+ */
+ public synchronized int getReceiveBufferSize()
+ throws SocketException{
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ int result = 0;
+ Object o = getImpl().getOption(SocketOptions.SO_RCVBUF);
+ if (o instanceof Integer) {
+ result = ((Integer)o).intValue();
+ }
+ return result;
+ }
+
+ /**
+ * Sets performance preferences for this ServerSocket.
+ *
+ * <p> Sockets use the TCP/IP protocol by default. Some implementations
+ * may offer alternative protocols which have different performance
+ * characteristics than TCP/IP. This method allows the application to
+ * express its own preferences as to how these tradeoffs should be made
+ * when the implementation chooses from the available protocols.
+ *
+ * <p> Performance preferences are described by three integers
+ * whose values indicate the relative importance of short connection time,
+ * low latency, and high bandwidth. The absolute values of the integers
+ * are irrelevant; in order to choose a protocol the values are simply
+ * compared, with larger values indicating stronger preferences. If the
+ * application prefers short connection time over both low latency and high
+ * bandwidth, for example, then it could invoke this method with the values
+ * {@code (1, 0, 0)}. If the application prefers high bandwidth above low
+ * latency, and low latency above short connection time, then it could
+ * invoke this method with the values {@code (0, 1, 2)}.
+ *
+ * <p> Invoking this method after this socket has been bound
+ * will have no effect. This implies that in order to use this capability
+ * requires the socket to be created with the no-argument constructor.
+ *
+ * @param connectionTime
+ * An {@code int} expressing the relative importance of a short
+ * connection time
+ *
+ * @param latency
+ * An {@code int} expressing the relative importance of low
+ * latency
+ *
+ * @param bandwidth
+ * An {@code int} expressing the relative importance of high
+ * bandwidth
+ *
+ * @since 1.5
+ */
+ public void setPerformancePreferences(int connectionTime,
+ int latency,
+ int bandwidth)
+ {
+ /* Not implemented yet */
+ }
+
+ // Android-added: getFileDescriptor$(), for testing / internal use.
+ /**
+ * @hide internal use only
+ */
+ public FileDescriptor getFileDescriptor$() {
+ return impl.getFileDescriptor();
+ }
+}
diff --git a/java/net/Socket.annotated.java b/java/net/Socket.annotated.java
new file mode 100644
index 0000000..e021788
--- /dev/null
+++ b/java/net/Socket.annotated.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package java.net;
+
+import java.nio.channels.SocketChannel;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+@SuppressWarnings({"unchecked", "deprecation", "all"})
+public class Socket implements java.io.Closeable {
+
+public Socket() { throw new RuntimeException("Stub!"); }
+
+public Socket(java.net.Proxy proxy) { throw new RuntimeException("Stub!"); }
+
+protected Socket(java.net.SocketImpl impl) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public Socket(java.lang.String host, int port) throws java.io.IOException, java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+
+public Socket(java.net.InetAddress address, int port) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public Socket(java.lang.String host, int port, java.net.InetAddress localAddr, int localPort) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public Socket(java.net.InetAddress address, int port, java.net.InetAddress localAddr, int localPort) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+@Deprecated
+public Socket(java.lang.String host, int port, boolean stream) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+@Deprecated
+public Socket(java.net.InetAddress host, int port, boolean stream) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public void connect(java.net.SocketAddress endpoint) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public void connect(java.net.SocketAddress endpoint, int timeout) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public void bind(java.net.SocketAddress bindpoint) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public java.net.InetAddress getInetAddress() { throw new RuntimeException("Stub!"); }
+
+public java.net.InetAddress getLocalAddress() { throw new RuntimeException("Stub!"); }
+
+public int getPort() { throw new RuntimeException("Stub!"); }
+
+public int getLocalPort() { throw new RuntimeException("Stub!"); }
+
+public java.net.SocketAddress getRemoteSocketAddress() { throw new RuntimeException("Stub!"); }
+
+public java.net.SocketAddress getLocalSocketAddress() { throw new RuntimeException("Stub!"); }
+
+public java.nio.channels.SocketChannel getChannel() { throw new RuntimeException("Stub!"); }
+
+public java.io.InputStream getInputStream() throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public java.io.OutputStream getOutputStream() throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public void setTcpNoDelay(boolean on) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public boolean getTcpNoDelay() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public void setSoLinger(boolean on, int linger) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public int getSoLinger() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public void sendUrgentData(int data) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public void setOOBInline(boolean on) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public boolean getOOBInline() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized void setSoTimeout(int timeout) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized int getSoTimeout() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized void setSendBufferSize(int size) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized int getSendBufferSize() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized void setReceiveBufferSize(int size) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized int getReceiveBufferSize() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public void setKeepAlive(boolean on) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public boolean getKeepAlive() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public void setTrafficClass(int tc) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public int getTrafficClass() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public void setReuseAddress(boolean on) throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public boolean getReuseAddress() throws java.net.SocketException { throw new RuntimeException("Stub!"); }
+
+public synchronized void close() throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public void shutdownInput() throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public void shutdownOutput() throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public java.lang.String toString() { throw new RuntimeException("Stub!"); }
+
+public boolean isConnected() { throw new RuntimeException("Stub!"); }
+
+public boolean isBound() { throw new RuntimeException("Stub!"); }
+
+public boolean isClosed() { throw new RuntimeException("Stub!"); }
+
+public boolean isInputShutdown() { throw new RuntimeException("Stub!"); }
+
+public boolean isOutputShutdown() { throw new RuntimeException("Stub!"); }
+
+public static synchronized void setSocketImplFactory(java.net.SocketImplFactory fac) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { throw new RuntimeException("Stub!"); }
+
[email protected]
[email protected]
+public java.io.FileDescriptor getFileDescriptor$() { throw new RuntimeException("Stub!"); }
+}
+
diff --git a/java/net/Socket.java b/java/net/Socket.java
new file mode 100644
index 0000000..bae1d1c
--- /dev/null
+++ b/java/net/Socket.java
@@ -0,0 +1,1783 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.FileDescriptor;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.nio.channels.SocketChannel;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+import java.security.PrivilegedAction;
+
+/**
+ * This class implements client sockets (also called just
+ * "sockets"). A socket is an endpoint for communication
+ * between two machines.
+ * <p>
+ * The actual work of the socket is performed by an instance of the
+ * {@code SocketImpl} class. An application, by changing
+ * the socket factory that creates the socket implementation,
+ * can configure itself to create sockets appropriate to the local
+ * firewall.
+ *
+ * @author unascribed
+ * @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)
+ * @see java.net.SocketImpl
+ * @see java.nio.channels.SocketChannel
+ * @since JDK1.0
+ */
+public
+class Socket implements java.io.Closeable {
+ /**
+ * Various states of this socket.
+ */
+ private boolean created = false;
+ private boolean bound = false;
+ private boolean connected = false;
+ private boolean closed = false;
+ private Object closeLock = new Object();
+ private boolean shutIn = false;
+ private boolean shutOut = false;
+
+ /**
+ * The implementation of this Socket.
+ */
+ SocketImpl impl;
+
+ /**
+ * Are we using an older SocketImpl?
+ */
+ private boolean oldImpl = false;
+
+ /**
+ * Creates an unconnected socket, with the
+ * system-default type of SocketImpl.
+ *
+ * @since JDK1.1
+ * @revised 1.4
+ */
+ public Socket() {
+ setImpl();
+ }
+
+ /**
+ * Creates an unconnected socket, specifying the type of proxy, if any,
+ * that should be used regardless of any other settings.
+ * <P>
+ * If there is a security manager, its {@code checkConnect} method
+ * is called with the proxy host address and port number
+ * as its arguments. This could result in a SecurityException.
+ * <P>
+ * Examples:
+ * <UL> <LI>{@code Socket s = new Socket(Proxy.NO_PROXY);} will create
+ * a plain socket ignoring any other proxy configuration.</LI>
+ * <LI>{@code Socket s = new Socket(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("socks.mydom.com", 1080)));}
+ * will create a socket connecting through the specified SOCKS proxy
+ * server.</LI>
+ * </UL>
+ *
+ * @param proxy a {@link java.net.Proxy Proxy} object specifying what kind
+ * of proxying should be used.
+ * @throws IllegalArgumentException if the proxy is of an invalid type
+ * or {@code null}.
+ * @throws SecurityException if a security manager is present and
+ * permission to connect to the proxy is
+ * denied.
+ * @see java.net.ProxySelector
+ * @see java.net.Proxy
+ *
+ * @since 1.5
+ */
+ public Socket(Proxy proxy) {
+ // Create a copy of Proxy as a security measure
+ if (proxy == null) {
+ throw new IllegalArgumentException("Invalid Proxy");
+ }
+ Proxy p = proxy == Proxy.NO_PROXY ? Proxy.NO_PROXY
+ : sun.net.ApplicationProxy.create(proxy);
+ Proxy.Type type = p.type();
+ // Android-changed: Removed HTTP proxy support.
+ // if (type == Proxy.Type.SOCKS || type == Proxy.Type.HTTP) {
+ if (type == Proxy.Type.SOCKS) {
+ SecurityManager security = System.getSecurityManager();
+ InetSocketAddress epoint = (InetSocketAddress) p.address();
+ if (epoint.getAddress() != null) {
+ checkAddress (epoint.getAddress(), "Socket");
+ }
+ if (security != null) {
+ if (epoint.isUnresolved())
+ epoint = new InetSocketAddress(epoint.getHostName(), epoint.getPort());
+ if (epoint.isUnresolved())
+ security.checkConnect(epoint.getHostName(), epoint.getPort());
+ else
+ security.checkConnect(epoint.getAddress().getHostAddress(),
+ epoint.getPort());
+ }
+ // Android-changed: Removed HTTP proxy support.
+ // impl = type == Proxy.Type.SOCKS ? new SocksSocketImpl(p)
+ // : new HttpConnectSocketImpl(p);
+ impl = new SocksSocketImpl(p);
+ impl.setSocket(this);
+ } else {
+ if (p == Proxy.NO_PROXY) {
+ if (factory == null) {
+ impl = new PlainSocketImpl();
+ impl.setSocket(this);
+ } else
+ setImpl();
+ } else
+ throw new IllegalArgumentException("Invalid Proxy");
+ }
+ }
+
+ /**
+ * Creates an unconnected Socket with a user-specified
+ * SocketImpl.
+ * <P>
+ * @param impl an instance of a <B>SocketImpl</B>
+ * the subclass wishes to use on the Socket.
+ *
+ * @exception SocketException if there is an error in the underlying protocol,
+ * such as a TCP error.
+ * @since JDK1.1
+ */
+ protected Socket(SocketImpl impl) throws SocketException {
+ this.impl = impl;
+ if (impl != null) {
+ checkOldImpl();
+ this.impl.setSocket(this);
+ }
+ }
+
+ /**
+ * Creates a stream socket and connects it to the specified port
+ * number on the named host.
+ * <p>
+ * If the specified host is {@code null} it is the equivalent of
+ * specifying the address as
+ * {@link java.net.InetAddress#getByName InetAddress.getByName}{@code (null)}.
+ * In other words, it is equivalent to specifying an address of the
+ * loopback interface. </p>
+ * <p>
+ * If the application has specified a server socket factory, that
+ * factory's {@code createSocketImpl} method is called to create
+ * the actual socket implementation. Otherwise a "plain" socket is created.
+ * <p>
+ * If there is a security manager, its
+ * {@code checkConnect} method is called
+ * with the host address and {@code port}
+ * as its arguments. This could result in a SecurityException.
+ *
+ * @param host the host name, or {@code null} for the loopback address.
+ * @param port the port number.
+ *
+ * @exception UnknownHostException if the IP address of
+ * the host could not be determined.
+ *
+ * @exception IOException if an I/O error occurs when creating the socket.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkConnect} method doesn't allow the operation.
+ * @exception IllegalArgumentException if the port parameter is outside
+ * the specified range of valid port values, which is between
+ * 0 and 65535, inclusive.
+ * @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)
+ * @see java.net.SocketImpl
+ * @see java.net.SocketImplFactory#createSocketImpl()
+ * @see SecurityManager#checkConnect
+ */
+ public Socket(String host, int port)
+ throws UnknownHostException, IOException
+ {
+ // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
+ this(InetAddress.getAllByName(host), port, (SocketAddress) null, true);
+ }
+
+ /**
+ * Creates a stream socket and connects it to the specified port
+ * number at the specified IP address.
+ * <p>
+ * If the application has specified a socket factory, that factory's
+ * {@code createSocketImpl} method is called to create the
+ * actual socket implementation. Otherwise a "plain" socket is created.
+ * <p>
+ * If there is a security manager, its
+ * {@code checkConnect} method is called
+ * with the host address and {@code port}
+ * as its arguments. This could result in a SecurityException.
+ *
+ * @param address the IP address.
+ * @param port the port number.
+ * @exception IOException if an I/O error occurs when creating the socket.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkConnect} method doesn't allow the operation.
+ * @exception IllegalArgumentException if the port parameter is outside
+ * the specified range of valid port values, which is between
+ * 0 and 65535, inclusive.
+ * @exception NullPointerException if {@code address} is null.
+ * @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)
+ * @see java.net.SocketImpl
+ * @see java.net.SocketImplFactory#createSocketImpl()
+ * @see SecurityManager#checkConnect
+ */
+ public Socket(InetAddress address, int port) throws IOException {
+ // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
+ this(nonNullAddress(address), port, (SocketAddress) null, true);
+ }
+
+ /**
+ * Creates a socket and connects it to the specified remote host on
+ * the specified remote port. The Socket will also bind() to the local
+ * address and port supplied.
+ * <p>
+ * If the specified host is {@code null} it is the equivalent of
+ * specifying the address as
+ * {@link java.net.InetAddress#getByName InetAddress.getByName}{@code (null)}.
+ * In other words, it is equivalent to specifying an address of the
+ * loopback interface. </p>
+ * <p>
+ * A local port number of {@code zero} will let the system pick up a
+ * free port in the {@code bind} operation.</p>
+ * <p>
+ * If there is a security manager, its
+ * {@code checkConnect} method is called
+ * with the host address and {@code port}
+ * as its arguments. This could result in a SecurityException.
+ *
+ * @param host the name of the remote host, or {@code null} for the loopback address.
+ * @param port the remote port
+ * @param localAddr the local address the socket is bound to, or
+ * {@code null} for the {@code anyLocal} address.
+ * @param localPort the local port the socket is bound to, or
+ * {@code zero} for a system selected free port.
+ * @exception IOException if an I/O error occurs when creating the socket.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkConnect} method doesn't allow the connection
+ * to the destination, or if its {@code checkListen} method
+ * doesn't allow the bind to the local port.
+ * @exception IllegalArgumentException if the port parameter or localPort
+ * parameter is outside the specified range of valid port values,
+ * which is between 0 and 65535, inclusive.
+ * @see SecurityManager#checkConnect
+ * @since JDK1.1
+ */
+ public Socket(String host, int port, InetAddress localAddr,
+ int localPort) throws IOException {
+ // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
+ this(InetAddress.getAllByName(host), port,
+ new InetSocketAddress(localAddr, localPort), true);
+ }
+
+ /**
+ * Creates a socket and connects it to the specified remote address on
+ * the specified remote port. The Socket will also bind() to the local
+ * address and port supplied.
+ * <p>
+ * If the specified local address is {@code null} it is the equivalent of
+ * specifying the address as the AnyLocal address
+ * (see {@link java.net.InetAddress#isAnyLocalAddress InetAddress.isAnyLocalAddress}{@code ()}).
+ * <p>
+ * A local port number of {@code zero} will let the system pick up a
+ * free port in the {@code bind} operation.</p>
+ * <p>
+ * If there is a security manager, its
+ * {@code checkConnect} method is called
+ * with the host address and {@code port}
+ * as its arguments. This could result in a SecurityException.
+ *
+ * @param address the remote address
+ * @param port the remote port
+ * @param localAddr the local address the socket is bound to, or
+ * {@code null} for the {@code anyLocal} address.
+ * @param localPort the local port the socket is bound to or
+ * {@code zero} for a system selected free port.
+ * @exception IOException if an I/O error occurs when creating the socket.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkConnect} method doesn't allow the connection
+ * to the destination, or if its {@code checkListen} method
+ * doesn't allow the bind to the local port.
+ * @exception IllegalArgumentException if the port parameter or localPort
+ * parameter is outside the specified range of valid port values,
+ * which is between 0 and 65535, inclusive.
+ * @exception NullPointerException if {@code address} is null.
+ * @see SecurityManager#checkConnect
+ * @since JDK1.1
+ */
+ public Socket(InetAddress address, int port, InetAddress localAddr,
+ int localPort) throws IOException {
+ // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
+ this(nonNullAddress(address), port,
+ new InetSocketAddress(localAddr, localPort), true);
+ }
+
+ /**
+ * Creates a stream socket and connects it to the specified port
+ * number on the named host.
+ * <p>
+ * If the specified host is {@code null} it is the equivalent of
+ * specifying the address as
+ * {@link java.net.InetAddress#getByName InetAddress.getByName}{@code (null)}.
+ * In other words, it is equivalent to specifying an address of the
+ * loopback interface. </p>
+ * <p>
+ * If the stream argument is {@code true}, this creates a
+ * stream socket. If the stream argument is {@code false}, it
+ * creates a datagram socket.
+ * <p>
+ * If the application has specified a server socket factory, that
+ * factory's {@code createSocketImpl} method is called to create
+ * the actual socket implementation. Otherwise a "plain" socket is created.
+ * <p>
+ * If there is a security manager, its
+ * {@code checkConnect} method is called
+ * with the host address and {@code port}
+ * as its arguments. This could result in a SecurityException.
+ * <p>
+ * If a UDP socket is used, TCP/IP related socket options will not apply.
+ *
+ * @param host the host name, or {@code null} for the loopback address.
+ * @param port the port number.
+ * @param stream a {@code boolean} indicating whether this is
+ * a stream socket or a datagram socket.
+ * @exception IOException if an I/O error occurs when creating the socket.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkConnect} method doesn't allow the operation.
+ * @exception IllegalArgumentException if the port parameter is outside
+ * the specified range of valid port values, which is between
+ * 0 and 65535, inclusive.
+ * @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)
+ * @see java.net.SocketImpl
+ * @see java.net.SocketImplFactory#createSocketImpl()
+ * @see SecurityManager#checkConnect
+ * @deprecated Use DatagramSocket instead for UDP transport.
+ */
+ @Deprecated
+ public Socket(String host, int port, boolean stream) throws IOException {
+ // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
+ this(InetAddress.getAllByName(host), port, (SocketAddress) null, stream);
+ }
+
+ /**
+ * Creates a socket and connects it to the specified port number at
+ * the specified IP address.
+ * <p>
+ * If the stream argument is {@code true}, this creates a
+ * stream socket. If the stream argument is {@code false}, it
+ * creates a datagram socket.
+ * <p>
+ * If the application has specified a server socket factory, that
+ * factory's {@code createSocketImpl} method is called to create
+ * the actual socket implementation. Otherwise a "plain" socket is created.
+ *
+ * <p>If there is a security manager, its
+ * {@code checkConnect} method is called
+ * with {@code host.getHostAddress()} and {@code port}
+ * as its arguments. This could result in a SecurityException.
+ * <p>
+ * If UDP socket is used, TCP/IP related socket options will not apply.
+ *
+ * @param host the IP address.
+ * @param port the port number.
+ * @param stream if {@code true}, create a stream socket;
+ * otherwise, create a datagram socket.
+ * @exception IOException if an I/O error occurs when creating the socket.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkConnect} method doesn't allow the operation.
+ * @exception IllegalArgumentException if the port parameter is outside
+ * the specified range of valid port values, which is between
+ * 0 and 65535, inclusive.
+ * @exception NullPointerException if {@code host} is null.
+ * @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)
+ * @see java.net.SocketImpl
+ * @see java.net.SocketImplFactory#createSocketImpl()
+ * @see SecurityManager#checkConnect
+ * @deprecated Use DatagramSocket instead for UDP transport.
+ */
+ @Deprecated
+ public Socket(InetAddress host, int port, boolean stream) throws IOException {
+ // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
+ this(nonNullAddress(host), port, new InetSocketAddress(0), stream);
+ }
+
+ // BEGIN Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
+ private static InetAddress[] nonNullAddress(InetAddress address) {
+ // backward compatibility
+ if (address == null)
+ throw new NullPointerException();
+
+ return new InetAddress[] { address };
+ }
+
+ private Socket(InetAddress[] addresses, int port, SocketAddress localAddr,
+ boolean stream) throws IOException {
+ if (addresses == null || addresses.length == 0) {
+ throw new SocketException("Impossible: empty address list");
+ }
+
+ for (int i = 0; i < addresses.length; i++) {
+ setImpl();
+ try {
+ InetSocketAddress address = new InetSocketAddress(addresses[i], port);
+ createImpl(stream);
+ if (localAddr != null) {
+ bind(localAddr);
+ }
+ connect(address);
+ break;
+ } catch (IOException | IllegalArgumentException | SecurityException e) {
+ try {
+ // Android-changed: Let ctor call impl.close() instead of overridable close().
+ // Subclasses may not expect a call to close() coming from this constructor.
+ impl.close();
+ closed = true;
+ } catch (IOException ce) {
+ e.addSuppressed(ce);
+ }
+
+ // Only stop on the last address.
+ if (i == addresses.length - 1) {
+ throw e;
+ }
+ }
+
+ // Discard the connection state and try again.
+ impl = null;
+ created = false;
+ bound = false;
+ closed = false;
+ }
+ }
+ // END Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
+
+ /**
+ * Creates the socket implementation.
+ *
+ * @param stream a {@code boolean} value : {@code true} for a TCP socket,
+ * {@code false} for UDP.
+ * @throws IOException if creation fails
+ * @since 1.4
+ */
+ void createImpl(boolean stream) throws SocketException {
+ if (impl == null)
+ setImpl();
+ try {
+ impl.create(stream);
+ created = true;
+ } catch (IOException e) {
+ throw new SocketException(e.getMessage());
+ }
+ }
+
+ private void checkOldImpl() {
+ if (impl == null)
+ return;
+ // SocketImpl.connect() is a protected method, therefore we need to use
+ // getDeclaredMethod, therefore we need permission to access the member
+
+ oldImpl = AccessController.doPrivileged
+ (new PrivilegedAction<Boolean>() {
+ public Boolean run() {
+ Class<?> clazz = impl.getClass();
+ while (true) {
+ try {
+ clazz.getDeclaredMethod("connect", SocketAddress.class, int.class);
+ return Boolean.FALSE;
+ } catch (NoSuchMethodException e) {
+ clazz = clazz.getSuperclass();
+ // java.net.SocketImpl class will always have this abstract method.
+ // If we have not found it by now in the hierarchy then it does not
+ // exist, we are an old style impl.
+ if (clazz.equals(java.net.SocketImpl.class)) {
+ return Boolean.TRUE;
+ }
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Sets impl to the system-default type of SocketImpl.
+ * @since 1.4
+ */
+ void setImpl() {
+ if (factory != null) {
+ impl = factory.createSocketImpl();
+ checkOldImpl();
+ } else {
+ // No need to do a checkOldImpl() here, we know it's an up to date
+ // SocketImpl!
+ impl = new SocksSocketImpl();
+ }
+ if (impl != null)
+ impl.setSocket(this);
+ }
+
+
+ /**
+ * Get the {@code SocketImpl} attached to this socket, creating
+ * it if necessary.
+ *
+ * @return the {@code SocketImpl} attached to that ServerSocket.
+ * @throws SocketException if creation fails
+ * @since 1.4
+ */
+ SocketImpl getImpl() throws SocketException {
+ if (!created)
+ createImpl(true);
+ return impl;
+ }
+
+ /**
+ * Connects this socket to the server.
+ *
+ * @param endpoint the {@code SocketAddress}
+ * @throws IOException if an error occurs during the connection
+ * @throws java.nio.channels.IllegalBlockingModeException
+ * if this socket has an associated channel,
+ * and the channel is in non-blocking mode
+ * @throws IllegalArgumentException if endpoint is null or is a
+ * SocketAddress subclass not supported by this socket
+ * @since 1.4
+ * @spec JSR-51
+ */
+ public void connect(SocketAddress endpoint) throws IOException {
+ connect(endpoint, 0);
+ }
+
+ /**
+ * Connects this socket to the server with a specified timeout value.
+ * A timeout of zero is interpreted as an infinite timeout. The connection
+ * will then block until established or an error occurs.
+ *
+ * @param endpoint the {@code SocketAddress}
+ * @param timeout the timeout value to be used in milliseconds.
+ * @throws IOException if an error occurs during the connection
+ * @throws SocketTimeoutException if timeout expires before connecting
+ * @throws java.nio.channels.IllegalBlockingModeException
+ * if this socket has an associated channel,
+ * and the channel is in non-blocking mode
+ * @throws IllegalArgumentException if endpoint is null or is a
+ * SocketAddress subclass not supported by this socket
+ * @since 1.4
+ * @spec JSR-51
+ */
+ public void connect(SocketAddress endpoint, int timeout) throws IOException {
+ if (endpoint == null)
+ throw new IllegalArgumentException("connect: The address can't be null");
+
+ if (timeout < 0)
+ throw new IllegalArgumentException("connect: timeout can't be negative");
+
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+
+ if (!oldImpl && isConnected())
+ throw new SocketException("already connected");
+
+ if (!(endpoint instanceof InetSocketAddress))
+ throw new IllegalArgumentException("Unsupported address type");
+
+ InetSocketAddress epoint = (InetSocketAddress) endpoint;
+ InetAddress addr = epoint.getAddress ();
+ int port = epoint.getPort();
+ checkAddress(addr, "connect");
+
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ if (epoint.isUnresolved())
+ security.checkConnect(epoint.getHostName(), port);
+ else
+ security.checkConnect(addr.getHostAddress(), port);
+ }
+ if (!created)
+ createImpl(true);
+ if (!oldImpl)
+ impl.connect(epoint, timeout);
+ else if (timeout == 0) {
+ if (epoint.isUnresolved())
+ impl.connect(addr.getHostName(), port);
+ else
+ impl.connect(addr, port);
+ } else
+ throw new UnsupportedOperationException("SocketImpl.connect(addr, timeout)");
+ connected = true;
+ /*
+ * If the socket was not bound before the connect, it is now because
+ * the kernel will have picked an ephemeral port & a local address
+ */
+ bound = true;
+ }
+
+ /**
+ * Binds the socket to a local address.
+ * <P>
+ * If the address is {@code null}, then the system will pick up
+ * an ephemeral port and a valid local address to bind the socket.
+ *
+ * @param bindpoint the {@code SocketAddress} to bind to
+ * @throws IOException if the bind operation fails, or if the socket
+ * is already bound.
+ * @throws IllegalArgumentException if bindpoint is a
+ * SocketAddress subclass not supported by this socket
+ * @throws SecurityException if a security manager exists and its
+ * {@code checkListen} method doesn't allow the bind
+ * to the local port.
+ *
+ * @since 1.4
+ * @see #isBound
+ */
+ public void bind(SocketAddress bindpoint) throws IOException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ if (!oldImpl && isBound())
+ throw new SocketException("Already bound");
+
+ if (bindpoint != null && (!(bindpoint instanceof InetSocketAddress)))
+ throw new IllegalArgumentException("Unsupported address type");
+ InetSocketAddress epoint = (InetSocketAddress) bindpoint;
+ if (epoint != null && epoint.isUnresolved())
+ throw new SocketException("Unresolved address");
+ if (epoint == null) {
+ epoint = new InetSocketAddress(0);
+ }
+ InetAddress addr = epoint.getAddress();
+ int port = epoint.getPort();
+ checkAddress (addr, "bind");
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkListen(port);
+ }
+ getImpl().bind (addr, port);
+ bound = true;
+ }
+
+ private void checkAddress (InetAddress addr, String op) {
+ if (addr == null) {
+ return;
+ }
+ if (!(addr instanceof Inet4Address || addr instanceof Inet6Address)) {
+ throw new IllegalArgumentException(op + ": invalid address type");
+ }
+ }
+
+ /**
+ * set the flags after an accept() call.
+ */
+ final void postAccept() {
+ connected = true;
+ created = true;
+ bound = true;
+ }
+
+ void setCreated() {
+ created = true;
+ }
+
+ void setBound() {
+ bound = true;
+ }
+
+ void setConnected() {
+ connected = true;
+ }
+
+ /**
+ * Returns the address to which the socket is connected.
+ * <p>
+ * If the socket was connected prior to being {@link #close closed},
+ * then this method will continue to return the connected address
+ * after the socket is closed.
+ *
+ * @return the remote IP address to which this socket is connected,
+ * or {@code null} if the socket is not connected.
+ */
+ public InetAddress getInetAddress() {
+ if (!isConnected())
+ return null;
+ try {
+ return getImpl().getInetAddress();
+ } catch (SocketException e) {
+ }
+ return null;
+ }
+
+ /**
+ * Gets the local address to which the socket is bound.
+ * <p>
+ * If there is a security manager set, its {@code checkConnect} method is
+ * called with the local address and {@code -1} as its arguments to see
+ * if the operation is allowed. If the operation is not allowed,
+ * the {@link InetAddress#getLoopbackAddress loopback} address is returned.
+ *
+ * @return the local address to which the socket is bound,
+ * the loopback address if denied by the security manager, or
+ * the wildcard address if the socket is closed or not bound yet.
+ * @since JDK1.1
+ *
+ * @see SecurityManager#checkConnect
+ */
+ public InetAddress getLocalAddress() {
+ // This is for backward compatibility
+ if (!isBound())
+ return InetAddress.anyLocalAddress();
+ InetAddress in = null;
+ try {
+ in = (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR);
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkConnect(in.getHostAddress(), -1);
+ if (in.isAnyLocalAddress()) {
+ in = InetAddress.anyLocalAddress();
+ }
+ } catch (SecurityException e) {
+ in = InetAddress.getLoopbackAddress();
+ } catch (Exception e) {
+ in = InetAddress.anyLocalAddress(); // "0.0.0.0"
+ }
+ return in;
+ }
+
+ /**
+ * Returns the remote port number to which this socket is connected.
+ * <p>
+ * If the socket was connected prior to being {@link #close closed},
+ * then this method will continue to return the connected port number
+ * after the socket is closed.
+ *
+ * @return the remote port number to which this socket is connected, or
+ * 0 if the socket is not connected yet.
+ */
+ public int getPort() {
+ if (!isConnected())
+ return 0;
+ try {
+ return getImpl().getPort();
+ } catch (SocketException e) {
+ // Shouldn't happen as we're connected
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the local port number to which this socket is bound.
+ * <p>
+ * If the socket was bound prior to being {@link #close closed},
+ * then this method will continue to return the local port number
+ * after the socket is closed.
+ *
+ * @return the local port number to which this socket is bound or -1
+ * if the socket is not bound yet.
+ */
+ public int getLocalPort() {
+ if (!isBound())
+ return -1;
+ try {
+ return getImpl().getLocalPort();
+ } catch(SocketException e) {
+ // shouldn't happen as we're bound
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the address of the endpoint this socket is connected to, or
+ * {@code null} if it is unconnected.
+ * <p>
+ * If the socket was connected prior to being {@link #close closed},
+ * then this method will continue to return the connected address
+ * after the socket is closed.
+ *
+
+ * @return a {@code SocketAddress} representing the remote endpoint of this
+ * socket, or {@code null} if it is not connected yet.
+ * @see #getInetAddress()
+ * @see #getPort()
+ * @see #connect(SocketAddress, int)
+ * @see #connect(SocketAddress)
+ * @since 1.4
+ */
+ public SocketAddress getRemoteSocketAddress() {
+ if (!isConnected())
+ return null;
+ return new InetSocketAddress(getInetAddress(), getPort());
+ }
+
+ /**
+ * Returns the address of the endpoint this socket is bound to.
+ * <p>
+ * If a socket bound to an endpoint represented by an
+ * {@code InetSocketAddress } is {@link #close closed},
+ * then this method will continue to return an {@code InetSocketAddress}
+ * after the socket is closed. In that case the returned
+ * {@code InetSocketAddress}'s address is the
+ * {@link InetAddress#isAnyLocalAddress wildcard} address
+ * and its port is the local port that it was bound to.
+ * <p>
+ * If there is a security manager set, its {@code checkConnect} method is
+ * called with the local address and {@code -1} as its arguments to see
+ * if the operation is allowed. If the operation is not allowed,
+ * a {@code SocketAddress} representing the
+ * {@link InetAddress#getLoopbackAddress loopback} address and the local
+ * port to which this socket is bound is returned.
+ *
+ * @return a {@code SocketAddress} representing the local endpoint of
+ * this socket, or a {@code SocketAddress} representing the
+ * loopback address if denied by the security manager, or
+ * {@code null} if the socket is not bound yet.
+ *
+ * @see #getLocalAddress()
+ * @see #getLocalPort()
+ * @see #bind(SocketAddress)
+ * @see SecurityManager#checkConnect
+ * @since 1.4
+ */
+
+ public SocketAddress getLocalSocketAddress() {
+ if (!isBound())
+ return null;
+ return new InetSocketAddress(getLocalAddress(), getLocalPort());
+ }
+
+ /**
+ * Returns the unique {@link java.nio.channels.SocketChannel SocketChannel}
+ * object associated with this socket, if any.
+ *
+ * <p> A socket will have a channel if, and only if, the channel itself was
+ * created via the {@link java.nio.channels.SocketChannel#open
+ * SocketChannel.open} or {@link
+ * java.nio.channels.ServerSocketChannel#accept ServerSocketChannel.accept}
+ * methods.
+ *
+ * @return the socket channel associated with this socket,
+ * or {@code null} if this socket was not created
+ * for a channel
+ *
+ * @since 1.4
+ * @spec JSR-51
+ */
+ public SocketChannel getChannel() {
+ return null;
+ }
+
+ /**
+ * Returns an input stream for this socket.
+ *
+ * <p> If this socket has an associated channel then the resulting input
+ * stream delegates all of its operations to the channel. If the channel
+ * is in non-blocking mode then the input stream's {@code read} operations
+ * will throw an {@link java.nio.channels.IllegalBlockingModeException}.
+ *
+ * <p>Under abnormal conditions the underlying connection may be
+ * broken by the remote host or the network software (for example
+ * a connection reset in the case of TCP connections). When a
+ * broken connection is detected by the network software the
+ * following applies to the returned input stream :-
+ *
+ * <ul>
+ *
+ * <li><p>The network software may discard bytes that are buffered
+ * by the socket. Bytes that aren't discarded by the network
+ * software can be read using {@link java.io.InputStream#read read}.
+ *
+ * <li><p>If there are no bytes buffered on the socket, or all
+ * buffered bytes have been consumed by
+ * {@link java.io.InputStream#read read}, then all subsequent
+ * calls to {@link java.io.InputStream#read read} will throw an
+ * {@link java.io.IOException IOException}.
+ *
+ * <li><p>If there are no bytes buffered on the socket, and the
+ * socket has not been closed using {@link #close close}, then
+ * {@link java.io.InputStream#available available} will
+ * return {@code 0}.
+ *
+ * </ul>
+ *
+ * <p> Closing the returned {@link java.io.InputStream InputStream}
+ * will close the associated socket.
+ *
+ * @return an input stream for reading bytes from this socket.
+ * @exception IOException if an I/O error occurs when creating the
+ * input stream, the socket is closed, the socket is
+ * not connected, or the socket input has been shutdown
+ * using {@link #shutdownInput()}
+ *
+ * @revised 1.4
+ * @spec JSR-51
+ */
+ public InputStream getInputStream() throws IOException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ if (!isConnected())
+ throw new SocketException("Socket is not connected");
+ if (isInputShutdown())
+ throw new SocketException("Socket input is shutdown");
+ final Socket s = this;
+ InputStream is = null;
+ try {
+ is = AccessController.doPrivileged(
+ new PrivilegedExceptionAction<InputStream>() {
+ public InputStream run() throws IOException {
+ return impl.getInputStream();
+ }
+ });
+ } catch (java.security.PrivilegedActionException e) {
+ throw (IOException) e.getException();
+ }
+ return is;
+ }
+
+ /**
+ * Returns an output stream for this socket.
+ *
+ * <p> If this socket has an associated channel then the resulting output
+ * stream delegates all of its operations to the channel. If the channel
+ * is in non-blocking mode then the output stream's {@code write}
+ * operations will throw an {@link
+ * java.nio.channels.IllegalBlockingModeException}.
+ *
+ * <p> Closing the returned {@link java.io.OutputStream OutputStream}
+ * will close the associated socket.
+ *
+ * @return an output stream for writing bytes to this socket.
+ * @exception IOException if an I/O error occurs when creating the
+ * output stream or if the socket is not connected.
+ * @revised 1.4
+ * @spec JSR-51
+ */
+ public OutputStream getOutputStream() throws IOException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ if (!isConnected())
+ throw new SocketException("Socket is not connected");
+ if (isOutputShutdown())
+ throw new SocketException("Socket output is shutdown");
+ final Socket s = this;
+ OutputStream os = null;
+ try {
+ os = AccessController.doPrivileged(
+ new PrivilegedExceptionAction<OutputStream>() {
+ public OutputStream run() throws IOException {
+ return impl.getOutputStream();
+ }
+ });
+ } catch (java.security.PrivilegedActionException e) {
+ throw (IOException) e.getException();
+ }
+ return os;
+ }
+
+ /**
+ * Enable/disable {@link SocketOptions#TCP_NODELAY TCP_NODELAY}
+ * (disable/enable Nagle's algorithm).
+ *
+ * @param on {@code true} to enable TCP_NODELAY,
+ * {@code false} to disable.
+ *
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ *
+ * @since JDK1.1
+ *
+ * @see #getTcpNoDelay()
+ */
+ public void setTcpNoDelay(boolean on) throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ getImpl().setOption(SocketOptions.TCP_NODELAY, Boolean.valueOf(on));
+ }
+
+ /**
+ * Tests if {@link SocketOptions#TCP_NODELAY TCP_NODELAY} is enabled.
+ *
+ * @return a {@code boolean} indicating whether or not
+ * {@link SocketOptions#TCP_NODELAY TCP_NODELAY} is enabled.
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ * @since JDK1.1
+ * @see #setTcpNoDelay(boolean)
+ */
+ public boolean getTcpNoDelay() throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ return ((Boolean) getImpl().getOption(SocketOptions.TCP_NODELAY)).booleanValue();
+ }
+
+ /**
+ * Enable/disable {@link SocketOptions#SO_LINGER SO_LINGER} with the
+ * specified linger time in seconds. The maximum timeout value is platform
+ * specific.
+ *
+ * The setting only affects socket close.
+ *
+ * @param on whether or not to linger on.
+ * @param linger how long to linger for, if on is true.
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ * @exception IllegalArgumentException if the linger value is negative.
+ * @since JDK1.1
+ * @see #getSoLinger()
+ */
+ public void setSoLinger(boolean on, int linger) throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ if (!on) {
+ getImpl().setOption(SocketOptions.SO_LINGER, new Boolean(on));
+ } else {
+ if (linger < 0) {
+ throw new IllegalArgumentException("invalid value for SO_LINGER");
+ }
+ if (linger > 65535)
+ linger = 65535;
+ getImpl().setOption(SocketOptions.SO_LINGER, new Integer(linger));
+ }
+ }
+
+ /**
+ * Returns setting for {@link SocketOptions#SO_LINGER SO_LINGER}.
+ * -1 returns implies that the
+ * option is disabled.
+ *
+ * The setting only affects socket close.
+ *
+ * @return the setting for {@link SocketOptions#SO_LINGER SO_LINGER}.
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ * @since JDK1.1
+ * @see #setSoLinger(boolean, int)
+ */
+ public int getSoLinger() throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ Object o = getImpl().getOption(SocketOptions.SO_LINGER);
+ if (o instanceof Integer) {
+ return ((Integer) o).intValue();
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Send one byte of urgent data on the socket. The byte to be sent is the lowest eight
+ * bits of the data parameter. The urgent byte is
+ * sent after any preceding writes to the socket OutputStream
+ * and before any future writes to the OutputStream.
+ * @param data The byte of data to send
+ * @exception IOException if there is an error
+ * sending the data.
+ * @since 1.4
+ */
+ public void sendUrgentData (int data) throws IOException {
+ // Android-changed: If the socket is closed, sendUrgentData should not create a new impl.
+ // Fail early to avoid leaking resources.
+ // http://b/31818400
+ if (isClosed()) {
+ throw new SocketException("Socket is closed");
+ }
+
+ if (!getImpl().supportsUrgentData ()) {
+ throw new SocketException ("Urgent data not supported");
+ }
+ getImpl().sendUrgentData (data);
+ }
+
+ /**
+ * Enable/disable {@link SocketOptions#SO_OOBINLINE SO_OOBINLINE}
+ * (receipt of TCP urgent data)
+ *
+ * By default, this option is disabled and TCP urgent data received on a
+ * socket is silently discarded. If the user wishes to receive urgent data, then
+ * this option must be enabled. When enabled, urgent data is received
+ * inline with normal data.
+ * <p>
+ * Note, only limited support is provided for handling incoming urgent
+ * data. In particular, no notification of incoming urgent data is provided
+ * and there is no capability to distinguish between normal data and urgent
+ * data unless provided by a higher level protocol.
+ *
+ * @param on {@code true} to enable
+ * {@link SocketOptions#SO_OOBINLINE SO_OOBINLINE},
+ * {@code false} to disable.
+ *
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ *
+ * @since 1.4
+ *
+ * @see #getOOBInline()
+ */
+ public void setOOBInline(boolean on) throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ getImpl().setOption(SocketOptions.SO_OOBINLINE, Boolean.valueOf(on));
+ }
+
+ /**
+ * Tests if {@link SocketOptions#SO_OOBINLINE SO_OOBINLINE} is enabled.
+ *
+ * @return a {@code boolean} indicating whether or not
+ * {@link SocketOptions#SO_OOBINLINE SO_OOBINLINE}is enabled.
+ *
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ * @since 1.4
+ * @see #setOOBInline(boolean)
+ */
+ public boolean getOOBInline() throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ return ((Boolean) getImpl().getOption(SocketOptions.SO_OOBINLINE)).booleanValue();
+ }
+
+ /**
+ * Enable/disable {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT}
+ * with the specified timeout, in milliseconds. With this option set
+ * to a non-zero timeout, a read() call on the InputStream associated with
+ * this Socket will block for only this amount of time. If the timeout
+ * expires, a <B>java.net.SocketTimeoutException</B> is raised, though the
+ * Socket is still valid. The option <B>must</B> be enabled
+ * prior to entering the blocking operation to have effect. The
+ * timeout must be {@code > 0}.
+ * A timeout of zero is interpreted as an infinite timeout.
+ *
+ * @param timeout the specified timeout, in milliseconds.
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ * @since JDK 1.1
+ * @see #getSoTimeout()
+ */
+ public synchronized void setSoTimeout(int timeout) throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ if (timeout < 0)
+ throw new IllegalArgumentException("timeout can't be negative");
+
+ getImpl().setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout));
+ }
+
+ /**
+ * Returns setting for {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT}.
+ * 0 returns implies that the option is disabled (i.e., timeout of infinity).
+ *
+ * @return the setting for {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT}
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ *
+ * @since JDK1.1
+ * @see #setSoTimeout(int)
+ */
+ public synchronized int getSoTimeout() throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ Object o = getImpl().getOption(SocketOptions.SO_TIMEOUT);
+ /* extra type safety */
+ if (o instanceof Integer) {
+ return ((Integer) o).intValue();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Sets the {@link SocketOptions#SO_SNDBUF SO_SNDBUF} option to the
+ * specified value for this {@code Socket}.
+ * The {@link SocketOptions#SO_SNDBUF SO_SNDBUF} option is used by the
+ * platform's networking code as a hint for the size to set the underlying
+ * network I/O buffers.
+ *
+ * <p>Because {@link SocketOptions#SO_SNDBUF SO_SNDBUF} is a hint,
+ * applications that want to verify what size the buffers were set to
+ * should call {@link #getSendBufferSize()}.
+ *
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ *
+ * @param size the size to which to set the send buffer
+ * size. This value must be greater than 0.
+ *
+ * @exception IllegalArgumentException if the
+ * value is 0 or is negative.
+ *
+ * @see #getSendBufferSize()
+ * @since 1.2
+ */
+ public synchronized void setSendBufferSize(int size)
+ throws SocketException{
+ if (!(size > 0)) {
+ throw new IllegalArgumentException("negative send size");
+ }
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ getImpl().setOption(SocketOptions.SO_SNDBUF, new Integer(size));
+ }
+
+ /**
+ * Get value of the {@link SocketOptions#SO_SNDBUF SO_SNDBUF} option
+ * for this {@code Socket}, that is the buffer size used by the platform
+ * for output on this {@code Socket}.
+ * @return the value of the {@link SocketOptions#SO_SNDBUF SO_SNDBUF}
+ * option for this {@code Socket}.
+ *
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ *
+ * @see #setSendBufferSize(int)
+ * @since 1.2
+ */
+ public synchronized int getSendBufferSize() throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ int result = 0;
+ Object o = getImpl().getOption(SocketOptions.SO_SNDBUF);
+ if (o instanceof Integer) {
+ result = ((Integer)o).intValue();
+ }
+ return result;
+ }
+
+ /**
+ * Sets the {@link SocketOptions#SO_RCVBUF SO_RCVBUF} option to the
+ * specified value for this {@code Socket}. The
+ * {@link SocketOptions#SO_RCVBUF SO_RCVBUF} option is
+ * used by the platform's networking code as a hint for the size to set
+ * the underlying network I/O buffers.
+ *
+ * <p>Increasing the receive buffer size can increase the performance of
+ * network I/O for high-volume connection, while decreasing it can
+ * help reduce the backlog of incoming data.
+ *
+ * <p>Because {@link SocketOptions#SO_RCVBUF SO_RCVBUF} is a hint,
+ * applications that want to verify what size the buffers were set to
+ * should call {@link #getReceiveBufferSize()}.
+ *
+ * <p>The value of {@link SocketOptions#SO_RCVBUF SO_RCVBUF} is also used
+ * to set the TCP receive window that is advertized to the remote peer.
+ * Generally, the window size can be modified at any time when a socket is
+ * connected. However, if a receive window larger than 64K is required then
+ * this must be requested <B>before</B> the socket is connected to the
+ * remote peer. There are two cases to be aware of:
+ * <ol>
+ * <li>For sockets accepted from a ServerSocket, this must be done by calling
+ * {@link ServerSocket#setReceiveBufferSize(int)} before the ServerSocket
+ * is bound to a local address.<p></li>
+ * <li>For client sockets, setReceiveBufferSize() must be called before
+ * connecting the socket to its remote peer.</li></ol>
+ * @param size the size to which to set the receive buffer
+ * size. This value must be greater than 0.
+ *
+ * @exception IllegalArgumentException if the value is 0 or is
+ * negative.
+ *
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ *
+ * @see #getReceiveBufferSize()
+ * @see ServerSocket#setReceiveBufferSize(int)
+ * @since 1.2
+ */
+ public synchronized void setReceiveBufferSize(int size)
+ throws SocketException{
+ if (size <= 0) {
+ throw new IllegalArgumentException("invalid receive size");
+ }
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ getImpl().setOption(SocketOptions.SO_RCVBUF, new Integer(size));
+ }
+
+ /**
+ * Gets the value of the {@link SocketOptions#SO_RCVBUF SO_RCVBUF} option
+ * for this {@code Socket}, that is the buffer size used by the platform
+ * for input on this {@code Socket}.
+ *
+ * @return the value of the {@link SocketOptions#SO_RCVBUF SO_RCVBUF}
+ * option for this {@code Socket}.
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ * @see #setReceiveBufferSize(int)
+ * @since 1.2
+ */
+ public synchronized int getReceiveBufferSize()
+ throws SocketException{
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ int result = 0;
+ Object o = getImpl().getOption(SocketOptions.SO_RCVBUF);
+ if (o instanceof Integer) {
+ result = ((Integer)o).intValue();
+ }
+ return result;
+ }
+
+ /**
+ * Enable/disable {@link SocketOptions#SO_KEEPALIVE SO_KEEPALIVE}.
+ *
+ * @param on whether or not to have socket keep alive turned on.
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ * @since 1.3
+ * @see #getKeepAlive()
+ */
+ public void setKeepAlive(boolean on) throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ getImpl().setOption(SocketOptions.SO_KEEPALIVE, Boolean.valueOf(on));
+ }
+
+ /**
+ * Tests if {@link SocketOptions#SO_KEEPALIVE SO_KEEPALIVE} is enabled.
+ *
+ * @return a {@code boolean} indicating whether or not
+ * {@link SocketOptions#SO_KEEPALIVE SO_KEEPALIVE} is enabled.
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ * @since 1.3
+ * @see #setKeepAlive(boolean)
+ */
+ public boolean getKeepAlive() throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ return ((Boolean) getImpl().getOption(SocketOptions.SO_KEEPALIVE)).booleanValue();
+ }
+
+ /**
+ * Sets traffic class or type-of-service octet in the IP
+ * header for packets sent from this Socket.
+ * As the underlying network implementation may ignore this
+ * value applications should consider it a hint.
+ *
+ * <P> The tc <B>must</B> be in the range {@code 0 <= tc <=
+ * 255} or an IllegalArgumentException will be thrown.
+ * <p>Notes:
+ * <p>For Internet Protocol v4 the value consists of an
+ * {@code integer}, the least significant 8 bits of which
+ * represent the value of the TOS octet in IP packets sent by
+ * the socket.
+ * RFC 1349 defines the TOS values as follows:
+ *
+ * <UL>
+ * <LI><CODE>IPTOS_LOWCOST (0x02)</CODE></LI>
+ * <LI><CODE>IPTOS_RELIABILITY (0x04)</CODE></LI>
+ * <LI><CODE>IPTOS_THROUGHPUT (0x08)</CODE></LI>
+ * <LI><CODE>IPTOS_LOWDELAY (0x10)</CODE></LI>
+ * </UL>
+ * The last low order bit is always ignored as this
+ * corresponds to the MBZ (must be zero) bit.
+ * <p>
+ * Setting bits in the precedence field may result in a
+ * SocketException indicating that the operation is not
+ * permitted.
+ * <p>
+ * As RFC 1122 section 4.2.4.2 indicates, a compliant TCP
+ * implementation should, but is not required to, let application
+ * change the TOS field during the lifetime of a connection.
+ * So whether the type-of-service field can be changed after the
+ * TCP connection has been established depends on the implementation
+ * in the underlying platform. Applications should not assume that
+ * they can change the TOS field after the connection.
+ * <p>
+ * For Internet Protocol v6 {@code tc} is the value that
+ * would be placed into the sin6_flowinfo field of the IP header.
+ *
+ * @param tc an {@code int} value for the bitset.
+ * @throws SocketException if there is an error setting the
+ * traffic class or type-of-service
+ * @since 1.4
+ * @see #getTrafficClass
+ * @see SocketOptions#IP_TOS
+ */
+ public void setTrafficClass(int tc) throws SocketException {
+ if (tc < 0 || tc > 255)
+ throw new IllegalArgumentException("tc is not in range 0 -- 255");
+
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ try {
+ getImpl().setOption(SocketOptions.IP_TOS, tc);
+ } catch (SocketException se) {
+ // not supported if socket already connected
+ // Solaris returns error in such cases
+ if(!isConnected())
+ throw se;
+ }
+ }
+
+ /**
+ * Gets traffic class or type-of-service in the IP header
+ * for packets sent from this Socket
+ * <p>
+ * As the underlying network implementation may ignore the
+ * traffic class or type-of-service set using {@link #setTrafficClass(int)}
+ * this method may return a different value than was previously
+ * set using the {@link #setTrafficClass(int)} method on this Socket.
+ *
+ * @return the traffic class or type-of-service already set
+ * @throws SocketException if there is an error obtaining the
+ * traffic class or type-of-service value.
+ * @since 1.4
+ * @see #setTrafficClass(int)
+ * @see SocketOptions#IP_TOS
+ */
+ public int getTrafficClass() throws SocketException {
+ // Android-changed: throw SocketException if the socket is already closed. http://b/31818400
+ if (isClosed()) {
+ throw new SocketException("Socket is closed");
+ }
+
+ return ((Integer) (getImpl().getOption(SocketOptions.IP_TOS))).intValue();
+ }
+
+ /**
+ * Enable/disable the {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR}
+ * socket option.
+ * <p>
+ * When a TCP connection is closed the connection may remain
+ * in a timeout state for a period of time after the connection
+ * is closed (typically known as the {@code TIME_WAIT} state
+ * or {@code 2MSL} wait state).
+ * For applications using a well known socket address or port
+ * it may not be possible to bind a socket to the required
+ * {@code SocketAddress} if there is a connection in the
+ * timeout state involving the socket address or port.
+ * <p>
+ * Enabling {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR}
+ * prior to binding the socket using {@link #bind(SocketAddress)} allows
+ * the socket to be bound even though a previous connection is in a timeout
+ * state.
+ * <p>
+ * When a {@code Socket} is created the initial setting
+ * of {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is disabled.
+ * <p>
+ * The behaviour when {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is
+ * enabled or disabled after a socket is bound (See {@link #isBound()})
+ * is not defined.
+ *
+ * @param on whether to enable or disable the socket option
+ * @exception SocketException if an error occurs enabling or
+ * disabling the {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR}
+ * socket option, or the socket is closed.
+ * @since 1.4
+ * @see #getReuseAddress()
+ * @see #bind(SocketAddress)
+ * @see #isClosed()
+ * @see #isBound()
+ */
+ public void setReuseAddress(boolean on) throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on));
+ }
+
+ /**
+ * Tests if {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is enabled.
+ *
+ * @return a {@code boolean} indicating whether or not
+ * {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is enabled.
+ * @exception SocketException if there is an error
+ * in the underlying protocol, such as a TCP error.
+ * @since 1.4
+ * @see #setReuseAddress(boolean)
+ */
+ public boolean getReuseAddress() throws SocketException {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ return ((Boolean) (getImpl().getOption(SocketOptions.SO_REUSEADDR))).booleanValue();
+ }
+
+ /**
+ * Closes this socket.
+ * <p>
+ * Any thread currently blocked in an I/O operation upon this socket
+ * will throw a {@link SocketException}.
+ * <p>
+ * Once a socket has been closed, it is not available for further networking
+ * use (i.e. can't be reconnected or rebound). A new socket needs to be
+ * created.
+ *
+ * <p> Closing this socket will also close the socket's
+ * {@link java.io.InputStream InputStream} and
+ * {@link java.io.OutputStream OutputStream}.
+ *
+ * <p> If this socket has an associated channel then the channel is closed
+ * as well.
+ *
+ * @exception IOException if an I/O error occurs when closing this socket.
+ * @revised 1.4
+ * @spec JSR-51
+ * @see #isClosed
+ */
+ public synchronized void close() throws IOException {
+ synchronized(closeLock) {
+ if (isClosed())
+ return;
+ if (created)
+ impl.close();
+ closed = true;
+ }
+ }
+
+ /**
+ * Places the input stream for this socket at "end of stream".
+ * Any data sent to the input stream side of the socket is acknowledged
+ * and then silently discarded.
+ * <p>
+ * If you read from a socket input stream after invoking this method on the
+ * socket, the stream's {@code available} method will return 0, and its
+ * {@code read} methods will return {@code -1} (end of stream).
+ *
+ * @exception IOException if an I/O error occurs when shutting down this
+ * socket.
+ *
+ * @since 1.3
+ * @see java.net.Socket#shutdownOutput()
+ * @see java.net.Socket#close()
+ * @see java.net.Socket#setSoLinger(boolean, int)
+ * @see #isInputShutdown
+ */
+ public void shutdownInput() throws IOException
+ {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ if (!isConnected())
+ throw new SocketException("Socket is not connected");
+ if (isInputShutdown())
+ throw new SocketException("Socket input is already shutdown");
+ getImpl().shutdownInput();
+ shutIn = true;
+ }
+
+ /**
+ * Disables the output stream for this socket.
+ * For a TCP socket, any previously written data will be sent
+ * followed by TCP's normal connection termination sequence.
+ *
+ * If you write to a socket output stream after invoking
+ * shutdownOutput() on the socket, the stream will throw
+ * an IOException.
+ *
+ * @exception IOException if an I/O error occurs when shutting down this
+ * socket.
+ *
+ * @since 1.3
+ * @see java.net.Socket#shutdownInput()
+ * @see java.net.Socket#close()
+ * @see java.net.Socket#setSoLinger(boolean, int)
+ * @see #isOutputShutdown
+ */
+ public void shutdownOutput() throws IOException
+ {
+ if (isClosed())
+ throw new SocketException("Socket is closed");
+ if (!isConnected())
+ throw new SocketException("Socket is not connected");
+ if (isOutputShutdown())
+ throw new SocketException("Socket output is already shutdown");
+ getImpl().shutdownOutput();
+ shutOut = true;
+ }
+
+ /**
+ * Converts this socket to a {@code String}.
+ *
+ * @return a string representation of this socket.
+ */
+ public String toString() {
+ try {
+ // Android-changed: change localport to localPort, and addr to address.
+ if (isConnected())
+ return "Socket[address=" + getImpl().getInetAddress() +
+ ",port=" + getImpl().getPort() +
+ ",localPort=" + getImpl().getLocalPort() + "]";
+ } catch (SocketException e) {
+ }
+ return "Socket[unconnected]";
+ }
+
+ /**
+ * Returns the connection state of the socket.
+ * <p>
+ * Note: Closing a socket doesn't clear its connection state, which means
+ * this method will return {@code true} for a closed socket
+ * (see {@link #isClosed()}) if it was successfuly connected prior
+ * to being closed.
+ *
+ * @return true if the socket was successfuly connected to a server
+ * @since 1.4
+ */
+ public boolean isConnected() {
+ // Before 1.3 Sockets were always connected during creation
+ return connected || oldImpl;
+ }
+
+ /**
+ * Returns the binding state of the socket.
+ * <p>
+ * Note: Closing a socket doesn't clear its binding state, which means
+ * this method will return {@code true} for a closed socket
+ * (see {@link #isClosed()}) if it was successfuly bound prior
+ * to being closed.
+ *
+ * @return true if the socket was successfuly bound to an address
+ * @since 1.4
+ * @see #bind
+ */
+ public boolean isBound() {
+ // Before 1.3 Sockets were always bound during creation
+ return bound || oldImpl;
+ }
+
+ /**
+ * Returns the closed state of the socket.
+ *
+ * @return true if the socket has been closed
+ * @since 1.4
+ * @see #close
+ */
+ public boolean isClosed() {
+ synchronized(closeLock) {
+ return closed;
+ }
+ }
+
+ /**
+ * Returns whether the read-half of the socket connection is closed.
+ *
+ * @return true if the input of the socket has been shutdown
+ * @since 1.4
+ * @see #shutdownInput
+ */
+ public boolean isInputShutdown() {
+ return shutIn;
+ }
+
+ /**
+ * Returns whether the write-half of the socket connection is closed.
+ *
+ * @return true if the output of the socket has been shutdown
+ * @since 1.4
+ * @see #shutdownOutput
+ */
+ public boolean isOutputShutdown() {
+ return shutOut;
+ }
+
+ /**
+ * The factory for all client sockets.
+ */
+ private static SocketImplFactory factory = null;
+
+ /**
+ * Sets the client socket implementation factory for the
+ * application. The factory can be specified only once.
+ * <p>
+ * When an application creates a new client socket, the socket
+ * implementation factory's {@code createSocketImpl} method is
+ * called to create the actual socket implementation.
+ * <p>
+ * Passing {@code null} to the method is a no-op unless the factory
+ * was already set.
+ * <p>If there is a security manager, this method first calls
+ * the security manager's {@code checkSetFactory} method
+ * to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ *
+ * @param fac the desired factory.
+ * @exception IOException if an I/O error occurs when setting the
+ * socket factory.
+ * @exception SocketException if the factory is already defined.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkSetFactory} method doesn't allow the operation.
+ * @see java.net.SocketImplFactory#createSocketImpl()
+ * @see SecurityManager#checkSetFactory
+ */
+ public static synchronized void setSocketImplFactory(SocketImplFactory fac)
+ throws IOException
+ {
+ if (factory != null) {
+ throw new SocketException("factory already defined");
+ }
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkSetFactory();
+ }
+ factory = fac;
+ }
+
+ /**
+ * Sets performance preferences for this socket.
+ *
+ * <p> Sockets use the TCP/IP protocol by default. Some implementations
+ * may offer alternative protocols which have different performance
+ * characteristics than TCP/IP. This method allows the application to
+ * express its own preferences as to how these tradeoffs should be made
+ * when the implementation chooses from the available protocols.
+ *
+ * <p> Performance preferences are described by three integers
+ * whose values indicate the relative importance of short connection time,
+ * low latency, and high bandwidth. The absolute values of the integers
+ * are irrelevant; in order to choose a protocol the values are simply
+ * compared, with larger values indicating stronger preferences. Negative
+ * values represent a lower priority than positive values. If the
+ * application prefers short connection time over both low latency and high
+ * bandwidth, for example, then it could invoke this method with the values
+ * {@code (1, 0, 0)}. If the application prefers high bandwidth above low
+ * latency, and low latency above short connection time, then it could
+ * invoke this method with the values {@code (0, 1, 2)}.
+ *
+ * <p> Invoking this method after this socket has been connected
+ * will have no effect.
+ *
+ * @param connectionTime
+ * An {@code int} expressing the relative importance of a short
+ * connection time
+ *
+ * @param latency
+ * An {@code int} expressing the relative importance of low
+ * latency
+ *
+ * @param bandwidth
+ * An {@code int} expressing the relative importance of high
+ * bandwidth
+ *
+ * @since 1.5
+ */
+ public void setPerformancePreferences(int connectionTime,
+ int latency,
+ int bandwidth)
+ {
+ /* Not implemented yet */
+ }
+
+ // Android-added: getFileDescriptor$() method for testing and internal use.
+ /**
+ * @hide internal use only
+ */
+ public FileDescriptor getFileDescriptor$() {
+ return impl.getFileDescriptor();
+ }
+}
diff --git a/java/net/SocketAddress.java b/java/net/SocketAddress.java
new file mode 100644
index 0000000..cfb014b
--- /dev/null
+++ b/java/net/SocketAddress.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.net;
+
+
+/**
+ *
+ * This class represents a Socket Address with no protocol attachment.
+ * As an abstract class, it is meant to be subclassed with a specific,
+ * protocol dependent, implementation.
+ * <p>
+ * It provides an immutable object used by sockets for binding, connecting, or
+ * as returned values.
+ *
+ * @see java.net.Socket
+ * @see java.net.ServerSocket
+ * @since 1.4
+ */
+public abstract class SocketAddress implements java.io.Serializable {
+
+ static final long serialVersionUID = 5215720748342549866L;
+
+}
diff --git a/java/net/SocketException.java b/java/net/SocketException.java
new file mode 100644
index 0000000..64ae771
--- /dev/null
+++ b/java/net/SocketException.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+
+/**
+ * Thrown to indicate that there is an error creating or accessing a Socket.
+ *
+ * @author Jonathan Payne
+ * @since JDK1.0
+ */
+public
+class SocketException extends IOException {
+ private static final long serialVersionUID = -5935874303556886934L;
+
+ /**
+ * Constructs a new {@code SocketException} with the
+ * specified detail message.
+ *
+ * @param msg the detail message.
+ */
+ public SocketException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructs a new {@code SocketException} with no detail message.
+ */
+ public SocketException() {
+ }
+
+ // BEGIN Android-added: SocketException ctor with cause for internal use.
+ /** @hide */
+ public SocketException(Throwable cause) {
+ super(cause);
+ }
+
+ /** @hide */
+ public SocketException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+ // END Android-added: SocketException ctor with cause for internal use.
+}
diff --git a/java/net/SocketImpl.annotated.java b/java/net/SocketImpl.annotated.java
new file mode 100644
index 0000000..2146d2f
--- /dev/null
+++ b/java/net/SocketImpl.annotated.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package java.net;
+
+import java.io.IOException;
+
+@SuppressWarnings({"unchecked", "deprecation", "all"})
+public abstract class SocketImpl implements java.net.SocketOptions {
+
+public SocketImpl() { throw new RuntimeException("Stub!"); }
+
+protected abstract void create(boolean stream) throws java.io.IOException;
+
+protected abstract void connect(java.lang.String host, int port) throws java.io.IOException;
+
+protected abstract void connect(java.net.InetAddress address, int port) throws java.io.IOException;
+
+protected abstract void connect(java.net.SocketAddress address, int timeout) throws java.io.IOException;
+
+protected abstract void bind(java.net.InetAddress host, int port) throws java.io.IOException;
+
+protected abstract void listen(int backlog) throws java.io.IOException;
+
+protected abstract void accept(java.net.SocketImpl s) throws java.io.IOException;
+
+protected abstract java.io.InputStream getInputStream() throws java.io.IOException;
+
+protected abstract java.io.OutputStream getOutputStream() throws java.io.IOException;
+
+protected abstract int available() throws java.io.IOException;
+
+protected abstract void close() throws java.io.IOException;
+
+protected void shutdownInput() throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+protected void shutdownOutput() throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+protected java.io.FileDescriptor getFileDescriptor() { throw new RuntimeException("Stub!"); }
+
[email protected]
+public java.io.FileDescriptor getFD$() { throw new RuntimeException("Stub!"); }
+
+protected java.net.InetAddress getInetAddress() { throw new RuntimeException("Stub!"); }
+
+protected int getPort() { throw new RuntimeException("Stub!"); }
+
+protected boolean supportsUrgentData() { throw new RuntimeException("Stub!"); }
+
+protected abstract void sendUrgentData(int data) throws java.io.IOException;
+
+protected int getLocalPort() { throw new RuntimeException("Stub!"); }
+
+public java.lang.String toString() { throw new RuntimeException("Stub!"); }
+
+protected void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { throw new RuntimeException("Stub!"); }
+
+protected java.net.InetAddress address;
+
+protected java.io.FileDescriptor fd;
+
+protected int localport;
+
+protected int port;
+}
+
diff --git a/java/net/SocketImpl.java b/java/net/SocketImpl.java
new file mode 100644
index 0000000..ade2630
--- /dev/null
+++ b/java/net/SocketImpl.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.FileDescriptor;
+
+/**
+ * The abstract class {@code SocketImpl} is a common superclass
+ * of all classes that actually implement sockets. It is used to
+ * create both client and server sockets.
+ * <p>
+ * A "plain" socket implements these methods exactly as
+ * described, without attempting to go through a firewall or proxy.
+ *
+ * @author unascribed
+ * @since JDK1.0
+ */
+public abstract class SocketImpl implements SocketOptions {
+ /**
+ * The actual Socket object.
+ */
+ Socket socket = null;
+ ServerSocket serverSocket = null;
+
+ /**
+ * The file descriptor object for this socket.
+ */
+ protected FileDescriptor fd;
+
+ /**
+ * The IP address of the remote end of this socket.
+ */
+ protected InetAddress address;
+
+ /**
+ * The port number on the remote host to which this socket is connected.
+ */
+ protected int port;
+
+ /**
+ * The local port number to which this socket is connected.
+ */
+ protected int localport;
+
+ /**
+ * Creates either a stream or a datagram socket.
+ *
+ * @param stream if {@code true}, create a stream socket;
+ * otherwise, create a datagram socket.
+ * @exception IOException if an I/O error occurs while creating the
+ * socket.
+ */
+ protected abstract void create(boolean stream) throws IOException;
+
+ /**
+ * Connects this socket to the specified port on the named host.
+ *
+ * @param host the name of the remote host.
+ * @param port the port number.
+ * @exception IOException if an I/O error occurs when connecting to the
+ * remote host.
+ */
+ protected abstract void connect(String host, int port) throws IOException;
+
+ /**
+ * Connects this socket to the specified port number on the specified host.
+ *
+ * @param address the IP address of the remote host.
+ * @param port the port number.
+ * @exception IOException if an I/O error occurs when attempting a
+ * connection.
+ */
+ protected abstract void connect(InetAddress address, int port) throws IOException;
+
+ /**
+ * Connects this socket to the specified port number on the specified host.
+ * A timeout of zero is interpreted as an infinite timeout. The connection
+ * will then block until established or an error occurs.
+ *
+ * @param address the Socket address of the remote host.
+ * @param timeout the timeout value, in milliseconds, or zero for no timeout.
+ * @exception IOException if an I/O error occurs when attempting a
+ * connection.
+ * @since 1.4
+ */
+ protected abstract void connect(SocketAddress address, int timeout) throws IOException;
+
+ /**
+ * Binds this socket to the specified local IP address and port number.
+ *
+ * @param host an IP address that belongs to a local interface.
+ * @param port the port number.
+ * @exception IOException if an I/O error occurs when binding this socket.
+ */
+ protected abstract void bind(InetAddress host, int port) throws IOException;
+
+ /**
+ * Sets the maximum queue length for incoming connection indications
+ * (a request to connect) to the {@code count} argument. If a
+ * connection indication arrives when the queue is full, the
+ * connection is refused.
+ *
+ * @param backlog the maximum length of the queue.
+ * @exception IOException if an I/O error occurs when creating the queue.
+ */
+ protected abstract void listen(int backlog) throws IOException;
+
+ /**
+ * Accepts a connection.
+ *
+ * @param s the accepted connection.
+ * @exception IOException if an I/O error occurs when accepting the
+ * connection.
+ */
+ protected abstract void accept(SocketImpl s) throws IOException;
+
+ /**
+ * Returns an input stream for this socket.
+ *
+ * @return a stream for reading from this socket.
+ * @exception IOException if an I/O error occurs when creating the
+ * input stream.
+ */
+ protected abstract InputStream getInputStream() throws IOException;
+
+ /**
+ * Returns an output stream for this socket.
+ *
+ * @return an output stream for writing to this socket.
+ * @exception IOException if an I/O error occurs when creating the
+ * output stream.
+ */
+ protected abstract OutputStream getOutputStream() throws IOException;
+
+ /**
+ * Returns the number of bytes that can be read from this socket
+ * without blocking.
+ *
+ * @return the number of bytes that can be read from this socket
+ * without blocking.
+ * @exception IOException if an I/O error occurs when determining the
+ * number of bytes available.
+ */
+ protected abstract int available() throws IOException;
+
+ /**
+ * Closes this socket.
+ *
+ * @exception IOException if an I/O error occurs when closing this socket.
+ */
+ protected abstract void close() throws IOException;
+
+ /**
+ * Places the input stream for this socket at "end of stream".
+ * Any data sent to this socket is acknowledged and then
+ * silently discarded.
+ *
+ * If you read from a socket input stream after invoking this method on the
+ * socket, the stream's {@code available} method will return 0, and its
+ * {@code read} methods will return {@code -1} (end of stream).
+ *
+ * @exception IOException if an I/O error occurs when shutting down this
+ * socket.
+ * @see java.net.Socket#shutdownOutput()
+ * @see java.net.Socket#close()
+ * @see java.net.Socket#setSoLinger(boolean, int)
+ * @since 1.3
+ */
+ protected void shutdownInput() throws IOException {
+ throw new IOException("Method not implemented!");
+ }
+
+ /**
+ * Disables the output stream for this socket.
+ * For a TCP socket, any previously written data will be sent
+ * followed by TCP's normal connection termination sequence.
+ *
+ * If you write to a socket output stream after invoking
+ * shutdownOutput() on the socket, the stream will throw
+ * an IOException.
+ *
+ * @exception IOException if an I/O error occurs when shutting down this
+ * socket.
+ * @see java.net.Socket#shutdownInput()
+ * @see java.net.Socket#close()
+ * @see java.net.Socket#setSoLinger(boolean, int)
+ * @since 1.3
+ */
+ protected void shutdownOutput() throws IOException {
+ throw new IOException("Method not implemented!");
+ }
+
+ /**
+ * Returns the value of this socket's {@code fd} field.
+ *
+ * @return the value of this socket's {@code fd} field.
+ * @see java.net.SocketImpl#fd
+ */
+ protected FileDescriptor getFileDescriptor() {
+ return fd;
+ }
+
+ // Android-added: getFD$() for testing.
+ /**
+ * @hide used by java.nio tests
+ */
+ public FileDescriptor getFD$() {
+ return fd;
+ }
+
+ /**
+ * Returns the value of this socket's {@code address} field.
+ *
+ * @return the value of this socket's {@code address} field.
+ * @see java.net.SocketImpl#address
+ */
+ protected InetAddress getInetAddress() {
+ return address;
+ }
+
+ /**
+ * Returns the value of this socket's {@code port} field.
+ *
+ * @return the value of this socket's {@code port} field.
+ * @see java.net.SocketImpl#port
+ */
+ protected int getPort() {
+ return port;
+ }
+
+ /**
+ * Returns whether or not this SocketImpl supports sending
+ * urgent data. By default, false is returned
+ * unless the method is overridden in a sub-class
+ *
+ * @return true if urgent data supported
+ * @see java.net.SocketImpl#address
+ * @since 1.4
+ */
+ protected boolean supportsUrgentData () {
+ return false; // must be overridden in sub-class
+ }
+
+ /**
+ * Send one byte of urgent data on the socket.
+ * The byte to be sent is the low eight bits of the parameter
+ * @param data The byte of data to send
+ * @exception IOException if there is an error
+ * sending the data.
+ * @since 1.4
+ */
+ protected abstract void sendUrgentData (int data) throws IOException;
+
+ /**
+ * Returns the value of this socket's {@code localport} field.
+ *
+ * @return the value of this socket's {@code localport} field.
+ * @see java.net.SocketImpl#localport
+ */
+ protected int getLocalPort() {
+ return localport;
+ }
+
+ void setSocket(Socket soc) {
+ this.socket = soc;
+ }
+
+ Socket getSocket() {
+ return socket;
+ }
+
+ void setServerSocket(ServerSocket soc) {
+ this.serverSocket = soc;
+ }
+
+ ServerSocket getServerSocket() {
+ return serverSocket;
+ }
+
+ /**
+ * Returns the address and port of this socket as a {@code String}.
+ *
+ * @return a string representation of this socket.
+ */
+ public String toString() {
+ return "Socket[addr=" + getInetAddress() +
+ ",port=" + getPort() + ",localport=" + getLocalPort() + "]";
+ }
+
+ void reset() throws IOException {
+ address = null;
+ port = 0;
+ localport = 0;
+ }
+
+ /**
+ * Sets performance preferences for this socket.
+ *
+ * <p> Sockets use the TCP/IP protocol by default. Some implementations
+ * may offer alternative protocols which have different performance
+ * characteristics than TCP/IP. This method allows the application to
+ * express its own preferences as to how these tradeoffs should be made
+ * when the implementation chooses from the available protocols.
+ *
+ * <p> Performance preferences are described by three integers
+ * whose values indicate the relative importance of short connection time,
+ * low latency, and high bandwidth. The absolute values of the integers
+ * are irrelevant; in order to choose a protocol the values are simply
+ * compared, with larger values indicating stronger preferences. Negative
+ * values represent a lower priority than positive values. If the
+ * application prefers short connection time over both low latency and high
+ * bandwidth, for example, then it could invoke this method with the values
+ * {@code (1, 0, 0)}. If the application prefers high bandwidth above low
+ * latency, and low latency above short connection time, then it could
+ * invoke this method with the values {@code (0, 1, 2)}.
+ *
+ * By default, this method does nothing, unless it is overridden in a
+ * a sub-class.
+ *
+ * @param connectionTime
+ * An {@code int} expressing the relative importance of a short
+ * connection time
+ *
+ * @param latency
+ * An {@code int} expressing the relative importance of low
+ * latency
+ *
+ * @param bandwidth
+ * An {@code int} expressing the relative importance of high
+ * bandwidth
+ *
+ * @since 1.5
+ */
+ protected void setPerformancePreferences(int connectionTime,
+ int latency,
+ int bandwidth)
+ {
+ /* Not implemented yet */
+ }
+
+ <T> void setOption(SocketOption<T> name, T value) throws IOException {
+ if (name == StandardSocketOptions.SO_KEEPALIVE) {
+ setOption(SocketOptions.SO_KEEPALIVE, value);
+ } else if (name == StandardSocketOptions.SO_SNDBUF) {
+ setOption(SocketOptions.SO_SNDBUF, value);
+ } else if (name == StandardSocketOptions.SO_RCVBUF) {
+ setOption(SocketOptions.SO_RCVBUF, value);
+ } else if (name == StandardSocketOptions.SO_REUSEADDR) {
+ setOption(SocketOptions.SO_REUSEADDR, value);
+ } else if (name == StandardSocketOptions.SO_LINGER) {
+ setOption(SocketOptions.SO_LINGER, value);
+ } else if (name == StandardSocketOptions.IP_TOS) {
+ setOption(SocketOptions.IP_TOS, value);
+ } else if (name == StandardSocketOptions.TCP_NODELAY) {
+ setOption(SocketOptions.TCP_NODELAY, value);
+ } else {
+ throw new UnsupportedOperationException("unsupported option");
+ }
+ }
+
+ <T> T getOption(SocketOption<T> name) throws IOException {
+ if (name == StandardSocketOptions.SO_KEEPALIVE) {
+ return (T)getOption(SocketOptions.SO_KEEPALIVE);
+ } else if (name == StandardSocketOptions.SO_SNDBUF) {
+ return (T)getOption(SocketOptions.SO_SNDBUF);
+ } else if (name == StandardSocketOptions.SO_RCVBUF) {
+ return (T)getOption(SocketOptions.SO_RCVBUF);
+ } else if (name == StandardSocketOptions.SO_REUSEADDR) {
+ return (T)getOption(SocketOptions.SO_REUSEADDR);
+ } else if (name == StandardSocketOptions.SO_LINGER) {
+ return (T)getOption(SocketOptions.SO_LINGER);
+ } else if (name == StandardSocketOptions.IP_TOS) {
+ return (T)getOption(SocketOptions.IP_TOS);
+ } else if (name == StandardSocketOptions.TCP_NODELAY) {
+ return (T)getOption(SocketOptions.TCP_NODELAY);
+ } else {
+ throw new UnsupportedOperationException("unsupported option");
+ }
+ }
+}
diff --git a/java/net/SocketImplFactory.java b/java/net/SocketImplFactory.java
new file mode 100644
index 0000000..7aa6363
--- /dev/null
+++ b/java/net/SocketImplFactory.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * This interface defines a factory for socket implementations. It
+ * is used by the classes {@code Socket} and
+ * {@code ServerSocket} to create actual socket
+ * implementations.
+ *
+ * @author Arthur van Hoff
+ * @see java.net.Socket
+ * @see java.net.ServerSocket
+ * @since JDK1.0
+ */
+public
+interface SocketImplFactory {
+ /**
+ * Creates a new {@code SocketImpl} instance.
+ *
+ * @return a new instance of {@code SocketImpl}.
+ * @see java.net.SocketImpl
+ */
+ SocketImpl createSocketImpl();
+}
diff --git a/java/net/SocketInputStream.java b/java/net/SocketInputStream.java
new file mode 100644
index 0000000..8d0e0c5
--- /dev/null
+++ b/java/net/SocketInputStream.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+
+import dalvik.system.BlockGuard;
+import sun.net.ConnectionResetException;
+
+/**
+ * This stream extends FileInputStream to implement a
+ * SocketInputStream. Note that this class should <b>NOT</b> be
+ * public.
+ *
+ * @author Jonathan Payne
+ * @author Arthur van Hoff
+ */
+class SocketInputStream extends FileInputStream
+{
+ // Android-removed: Android doesn't need to call native init.
+ // static {
+ // init();
+ //}
+
+ private boolean eof;
+ private AbstractPlainSocketImpl impl = null;
+ private byte temp[];
+ private Socket socket = null;
+
+ /**
+ * Creates a new SocketInputStream. Can only be called
+ * by a Socket. This method needs to hang on to the owner Socket so
+ * that the fd will not be closed.
+ * @param impl the implemented socket input stream
+ */
+ SocketInputStream(AbstractPlainSocketImpl impl) throws IOException {
+ super(impl.getFileDescriptor());
+ this.impl = impl;
+ socket = impl.getSocket();
+ }
+
+ /**
+ * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
+ * object associated with this file input stream.</p>
+ *
+ * The {@code getChannel} method of {@code SocketInputStream}
+ * returns {@code null} since it is a socket based stream.</p>
+ *
+ * @return the file channel associated with this file input stream
+ *
+ * @since 1.4
+ * @spec JSR-51
+ */
+ public final FileChannel getChannel() {
+ return null;
+ }
+
+ /**
+ * Reads into an array of bytes at the specified offset using
+ * the received socket primitive.
+ * @param fd the FileDescriptor
+ * @param b the buffer into which the data is read
+ * @param off the start offset of the data
+ * @param len the maximum number of bytes read
+ * @param timeout the read timeout in ms
+ * @return the actual number of bytes read, -1 is
+ * returned when the end of the stream is reached.
+ * @exception IOException If an I/O error has occurred.
+ */
+ private native int socketRead0(FileDescriptor fd,
+ byte b[], int off, int len,
+ int timeout)
+ throws IOException;
+
+ // wrap native call to allow instrumentation
+ /**
+ * Reads into an array of bytes at the specified offset using
+ * the received socket primitive.
+ * @param fd the FileDescriptor
+ * @param b the buffer into which the data is read
+ * @param off the start offset of the data
+ * @param len the maximum number of bytes read
+ * @param timeout the read timeout in ms
+ * @return the actual number of bytes read, -1 is
+ * returned when the end of the stream is reached.
+ * @exception IOException If an I/O error has occurred.
+ */
+ private int socketRead(FileDescriptor fd,
+ byte b[], int off, int len,
+ int timeout)
+ throws IOException {
+ return socketRead0(fd, b, off, len, timeout);
+ }
+
+ /**
+ * Reads into a byte array data from the socket.
+ * @param b the buffer into which the data is read
+ * @return the actual number of bytes read, -1 is
+ * returned when the end of the stream is reached.
+ * @exception IOException If an I/O error has occurred.
+ */
+ public int read(byte b[]) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ /**
+ * Reads into a byte array <i>b</i> at offset <i>off</i>,
+ * <i>length</i> bytes of data.
+ * @param b the buffer into which the data is read
+ * @param off the start offset of the data
+ * @param length the maximum number of bytes read
+ * @return the actual number of bytes read, -1 is
+ * returned when the end of the stream is reached.
+ * @exception IOException If an I/O error has occurred.
+ */
+ public int read(byte b[], int off, int length) throws IOException {
+ return read(b, off, length, impl.getTimeout());
+ }
+
+ int read(byte b[], int off, int length, int timeout) throws IOException {
+ int n;
+
+ // EOF already encountered
+ if (eof) {
+ return -1;
+ }
+
+ // connection reset
+ if (impl.isConnectionReset()) {
+ throw new SocketException("Connection reset");
+ }
+
+ // bounds check
+ if (length <= 0 || off < 0 || length > b.length - off) {
+ if (length == 0) {
+ return 0;
+ }
+ throw new ArrayIndexOutOfBoundsException("length == " + length
+ + " off == " + off + " buffer length == " + b.length);
+ }
+
+ boolean gotReset = false;
+
+ // acquire file descriptor and do the read
+ FileDescriptor fd = impl.acquireFD();
+ try {
+ // Android-added: Check BlockGuard policy in read().
+ BlockGuard.getThreadPolicy().onNetwork();
+ n = socketRead(fd, b, off, length, timeout);
+ if (n > 0) {
+ return n;
+ }
+ } catch (ConnectionResetException rstExc) {
+ gotReset = true;
+ } finally {
+ impl.releaseFD();
+ }
+
+ /*
+ * We receive a "connection reset" but there may be bytes still
+ * buffered on the socket
+ */
+ if (gotReset) {
+ impl.setConnectionResetPending();
+ impl.acquireFD();
+ try {
+ n = socketRead(fd, b, off, length, timeout);
+ if (n > 0) {
+ return n;
+ }
+ } catch (ConnectionResetException rstExc) {
+ } finally {
+ impl.releaseFD();
+ }
+ }
+
+ /*
+ * If we get here we are at EOF, the socket has been closed,
+ * or the connection has been reset.
+ */
+ if (impl.isClosedOrPending()) {
+ throw new SocketException("Socket closed");
+ }
+ if (impl.isConnectionResetPending()) {
+ impl.setConnectionReset();
+ }
+ if (impl.isConnectionReset()) {
+ throw new SocketException("Connection reset");
+ }
+ eof = true;
+ return -1;
+ }
+
+ /**
+ * Reads a single byte from the socket.
+ */
+ public int read() throws IOException {
+ if (eof) {
+ return -1;
+ }
+ temp = new byte[1];
+ int n = read(temp, 0, 1);
+ if (n <= 0) {
+ return -1;
+ }
+ return temp[0] & 0xff;
+ }
+
+ /**
+ * Skips n bytes of input.
+ * @param numbytes the number of bytes to skip
+ * @return the actual number of bytes skipped.
+ * @exception IOException If an I/O error has occurred.
+ */
+ public long skip(long numbytes) throws IOException {
+ if (numbytes <= 0) {
+ return 0;
+ }
+ long n = numbytes;
+ int buflen = (int) Math.min(1024, n);
+ byte data[] = new byte[buflen];
+ while (n > 0) {
+ int r = read(data, 0, (int) Math.min((long) buflen, n));
+ if (r < 0) {
+ break;
+ }
+ n -= r;
+ }
+ return numbytes - n;
+ }
+
+ /**
+ * Returns the number of bytes that can be read without blocking.
+ * @return the number of immediately available bytes
+ */
+ public int available() throws IOException {
+ // Android-changed: Bug fix, if eof == true, we must indicate that we
+ // have 0 bytes available.
+ if (eof) {
+ return 0;
+ } else {
+ return impl.available();
+ }
+ }
+
+ /**
+ * Closes the stream.
+ */
+ private boolean closing = false;
+ public void close() throws IOException {
+ // Prevent recursion. See BugId 4484411
+ if (closing)
+ return;
+ closing = true;
+ if (socket != null) {
+ if (!socket.isClosed())
+ socket.close();
+ } else
+ impl.close();
+ closing = false;
+ }
+
+ void setEOF(boolean eof) {
+ this.eof = eof;
+ }
+
+ /**
+ * Overrides finalize, the fd is closed by the Socket.
+ */
+ protected void finalize() {}
+
+ // Android-removed: Android doesn't need native init.
+ /*
+ * Perform class load-time initializations.
+ *
+ private native static void init();
+ */
+}
diff --git a/java/net/SocketOption.java b/java/net/SocketOption.java
new file mode 100644
index 0000000..2ccf57f
--- /dev/null
+++ b/java/net/SocketOption.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * A socket option associated with a socket.
+ *
+ * <p> In the {@link java.nio.channels channels} package, the {@link
+ * java.nio.channels.NetworkChannel} interface defines the {@link
+ * java.nio.channels.NetworkChannel#setOption(SocketOption,Object) setOption}
+ * and {@link java.nio.channels.NetworkChannel#getOption(SocketOption) getOption}
+ * methods to set and query the channel's socket options.
+ *
+ * @param <T> The type of the socket option value.
+ *
+ * @since 1.7
+ *
+ * @see StandardSocketOptions
+ */
+
+public interface SocketOption<T> {
+
+ /**
+ * Returns the name of the socket option.
+ *
+ * @return the name of the socket option
+ */
+ String name();
+
+ /**
+ * Returns the type of the socket option value.
+ *
+ * @return the type of the socket option value
+ */
+ Class<T> type();
+}
diff --git a/java/net/SocketOptions.java b/java/net/SocketOptions.java
new file mode 100644
index 0000000..7e1b0fc
--- /dev/null
+++ b/java/net/SocketOptions.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.lang.annotation.Native;
+
+/**
+ * Interface of methods to get/set socket options. This interface is
+ * implemented by: <B>SocketImpl</B> and <B>DatagramSocketImpl</B>.
+ * Subclasses of these should override the methods
+ * of this interface in order to support their own options.
+ * <P>
+ * The methods and constants which specify options in this interface are
+ * for implementation only. If you're not subclassing SocketImpl or
+ * DatagramSocketImpl, <B>you won't use these directly.</B> There are
+ * type-safe methods to get/set each of these options in Socket, ServerSocket,
+ * DatagramSocket and MulticastSocket.
+ * <P>
+ * @author David Brown
+ */
+
+
+public interface SocketOptions {
+
+ /**
+ * Enable/disable the option specified by <I>optID</I>. If the option
+ * is to be enabled, and it takes an option-specific "value", this is
+ * passed in <I>value</I>. The actual type of value is option-specific,
+ * and it is an error to pass something that isn't of the expected type:
+ * <BR><PRE>
+ * SocketImpl s;
+ * ...
+ * s.setOption(SO_LINGER, new Integer(10));
+ * // OK - set SO_LINGER w/ timeout of 10 sec.
+ * s.setOption(SO_LINGER, new Double(10));
+ * // ERROR - expects java.lang.Integer
+ *</PRE>
+ * If the requested option is binary, it can be set using this method by
+ * a java.lang.Boolean:
+ * <BR><PRE>
+ * s.setOption(TCP_NODELAY, new Boolean(true));
+ * // OK - enables TCP_NODELAY, a binary option
+ * </PRE>
+ * <BR>
+ * Any option can be disabled using this method with a Boolean(false):
+ * <BR><PRE>
+ * s.setOption(TCP_NODELAY, new Boolean(false));
+ * // OK - disables TCP_NODELAY
+ * s.setOption(SO_LINGER, new Boolean(false));
+ * // OK - disables SO_LINGER
+ * </PRE>
+ * <BR>
+ * For an option that has a notion of on and off, and requires
+ * a non-boolean parameter, setting its value to anything other than
+ * <I>Boolean(false)</I> implicitly enables it.
+ * <BR>
+ * Throws SocketException if the option is unrecognized,
+ * the socket is closed, or some low-level error occurred
+ * <BR>
+ * @param optID identifies the option
+ * @param value the parameter of the socket option
+ * @throws SocketException if the option is unrecognized,
+ * the socket is closed, or some low-level error occurred
+ * @see #getOption(int)
+ */
+ public void
+ setOption(int optID, Object value) throws SocketException;
+
+ /**
+ * Fetch the value of an option.
+ * Binary options will return java.lang.Boolean(true)
+ * if enabled, java.lang.Boolean(false) if disabled, e.g.:
+ * <BR><PRE>
+ * SocketImpl s;
+ * ...
+ * Boolean noDelay = (Boolean)(s.getOption(TCP_NODELAY));
+ * if (noDelay.booleanValue()) {
+ * // true if TCP_NODELAY is enabled...
+ * ...
+ * }
+ * </PRE>
+ * <P>
+ * For options that take a particular type as a parameter,
+ * getOption(int) will return the parameter's value, else
+ * it will return java.lang.Boolean(false):
+ * <PRE>
+ * Object o = s.getOption(SO_LINGER);
+ * if (o instanceof Integer) {
+ * System.out.print("Linger time is " + ((Integer)o).intValue());
+ * } else {
+ * // the true type of o is java.lang.Boolean(false);
+ * }
+ * </PRE>
+ *
+ * @param optID an {@code int} identifying the option to fetch
+ * @return the value of the option
+ * @throws SocketException if the socket is closed
+ * @throws SocketException if <I>optID</I> is unknown along the
+ * protocol stack (including the SocketImpl)
+ * @see #setOption(int, java.lang.Object)
+ */
+ public Object getOption(int optID) throws SocketException;
+
+ /**
+ * The java-supported BSD-style options.
+ */
+
+ /**
+ * Disable Nagle's algorithm for this connection. Written data
+ * to the network is not buffered pending acknowledgement of
+ * previously written data.
+ *<P>
+ * Valid for TCP only: SocketImpl.
+ *
+ * @see Socket#setTcpNoDelay
+ * @see Socket#getTcpNoDelay
+ */
+
+ @Native public final static int TCP_NODELAY = 0x0001;
+
+ /**
+ * Fetch the local address binding of a socket (this option cannot
+ * be "set" only "gotten", since sockets are bound at creation time,
+ * and so the locally bound address cannot be changed). The default local
+ * address of a socket is INADDR_ANY, meaning any local address on a
+ * multi-homed host. A multi-homed host can use this option to accept
+ * connections to only one of its addresses (in the case of a
+ * ServerSocket or DatagramSocket), or to specify its return address
+ * to the peer (for a Socket or DatagramSocket). The parameter of
+ * this option is an InetAddress.
+ * <P>
+ * This option <B>must</B> be specified in the constructor.
+ * <P>
+ * Valid for: SocketImpl, DatagramSocketImpl
+ *
+ * @see Socket#getLocalAddress
+ * @see DatagramSocket#getLocalAddress
+ */
+
+ @Native public final static int SO_BINDADDR = 0x000F;
+
+ /** Sets SO_REUSEADDR for a socket. This is used only for MulticastSockets
+ * in java, and it is set by default for MulticastSockets.
+ * <P>
+ * Valid for: DatagramSocketImpl
+ */
+
+ @Native public final static int SO_REUSEADDR = 0x04;
+
+ /**
+ * Sets SO_BROADCAST for a socket. This option enables and disables
+ * the ability of the process to send broadcast messages. It is supported
+ * for only datagram sockets and only on networks that support
+ * the concept of a broadcast message (e.g. Ethernet, token ring, etc.),
+ * and it is set by default for DatagramSockets.
+ * @since 1.4
+ */
+
+ @Native public final static int SO_BROADCAST = 0x0020;
+
+ /** Set which outgoing interface on which to send multicast packets.
+ * Useful on hosts with multiple network interfaces, where applications
+ * want to use other than the system default. Takes/returns an InetAddress.
+ * <P>
+ * Valid for Multicast: DatagramSocketImpl
+ *
+ * @see MulticastSocket#setInterface(InetAddress)
+ * @see MulticastSocket#getInterface()
+ */
+
+ @Native public final static int IP_MULTICAST_IF = 0x10;
+
+ /** Same as above. This option is introduced so that the behaviour
+ * with IP_MULTICAST_IF will be kept the same as before, while
+ * this new option can support setting outgoing interfaces with either
+ * IPv4 and IPv6 addresses.
+ *
+ * NOTE: make sure there is no conflict with this
+ * @see MulticastSocket#setNetworkInterface(NetworkInterface)
+ * @see MulticastSocket#getNetworkInterface()
+ * @since 1.4
+ */
+ @Native public final static int IP_MULTICAST_IF2 = 0x1f;
+
+ /**
+ * This option enables or disables local loopback of multicast datagrams.
+ * This option is enabled by default for Multicast Sockets.
+ * @since 1.4
+ */
+
+ @Native public final static int IP_MULTICAST_LOOP = 0x12;
+
+ /**
+ * This option sets the type-of-service or traffic class field
+ * in the IP header for a TCP or UDP socket.
+ * @since 1.4
+ */
+
+ @Native public final static int IP_TOS = 0x3;
+
+ /**
+ * Specify a linger-on-close timeout. This option disables/enables
+ * immediate return from a <B>close()</B> of a TCP Socket. Enabling
+ * this option with a non-zero Integer <I>timeout</I> means that a
+ * <B>close()</B> will block pending the transmission and acknowledgement
+ * of all data written to the peer, at which point the socket is closed
+ * <I>gracefully</I>. Upon reaching the linger timeout, the socket is
+ * closed <I>forcefully</I>, with a TCP RST. Enabling the option with a
+ * timeout of zero does a forceful close immediately. If the specified
+ * timeout value exceeds 65,535 it will be reduced to 65,535.
+ * <P>
+ * Valid only for TCP: SocketImpl
+ *
+ * @see Socket#setSoLinger
+ * @see Socket#getSoLinger
+ */
+ @Native public final static int SO_LINGER = 0x0080;
+
+ /** Set a timeout on blocking Socket operations:
+ * <PRE>
+ * ServerSocket.accept();
+ * SocketInputStream.read();
+ * DatagramSocket.receive();
+ * </PRE>
+ *
+ * <P> The option must be set prior to entering a blocking
+ * operation to take effect. If the timeout expires and the
+ * operation would continue to block,
+ * <B>java.io.InterruptedIOException</B> is raised. The Socket is
+ * not closed in this case.
+ *
+ * <P> Valid for all sockets: SocketImpl, DatagramSocketImpl
+ *
+ * @see Socket#setSoTimeout
+ * @see ServerSocket#setSoTimeout
+ * @see DatagramSocket#setSoTimeout
+ */
+ @Native public final static int SO_TIMEOUT = 0x1006;
+
+ /**
+ * Set a hint the size of the underlying buffers used by the
+ * platform for outgoing network I/O. When used in set, this is a
+ * suggestion to the kernel from the application about the size of
+ * buffers to use for the data to be sent over the socket. When
+ * used in get, this must return the size of the buffer actually
+ * used by the platform when sending out data on this socket.
+ *
+ * Valid for all sockets: SocketImpl, DatagramSocketImpl
+ *
+ * @see Socket#setSendBufferSize
+ * @see Socket#getSendBufferSize
+ * @see DatagramSocket#setSendBufferSize
+ * @see DatagramSocket#getSendBufferSize
+ */
+ @Native public final static int SO_SNDBUF = 0x1001;
+
+ /**
+ * Set a hint the size of the underlying buffers used by the
+ * platform for incoming network I/O. When used in set, this is a
+ * suggestion to the kernel from the application about the size of
+ * buffers to use for the data to be received over the
+ * socket. When used in get, this must return the size of the
+ * buffer actually used by the platform when receiving in data on
+ * this socket.
+ *
+ * Valid for all sockets: SocketImpl, DatagramSocketImpl
+ *
+ * @see Socket#setReceiveBufferSize
+ * @see Socket#getReceiveBufferSize
+ * @see DatagramSocket#setReceiveBufferSize
+ * @see DatagramSocket#getReceiveBufferSize
+ */
+ @Native public final static int SO_RCVBUF = 0x1002;
+
+ /**
+ * When the keepalive option is set for a TCP socket and no data
+ * has been exchanged across the socket in either direction for
+ * 2 hours (NOTE: the actual value is implementation dependent),
+ * TCP automatically sends a keepalive probe to the peer. This probe is a
+ * TCP segment to which the peer must respond.
+ * One of three responses is expected:
+ * 1. The peer responds with the expected ACK. The application is not
+ * notified (since everything is OK). TCP will send another probe
+ * following another 2 hours of inactivity.
+ * 2. The peer responds with an RST, which tells the local TCP that
+ * the peer host has crashed and rebooted. The socket is closed.
+ * 3. There is no response from the peer. The socket is closed.
+ *
+ * The purpose of this option is to detect if the peer host crashes.
+ *
+ * Valid only for TCP socket: SocketImpl
+ *
+ * @see Socket#setKeepAlive
+ * @see Socket#getKeepAlive
+ */
+ @Native public final static int SO_KEEPALIVE = 0x0008;
+
+ /**
+ * When the OOBINLINE option is set, any TCP urgent data received on
+ * the socket will be received through the socket input stream.
+ * When the option is disabled (which is the default) urgent data
+ * is silently discarded.
+ *
+ * @see Socket#setOOBInline
+ * @see Socket#getOOBInline
+ */
+ @Native public final static int SO_OOBINLINE = 0x1003;
+}
diff --git a/java/net/SocketOutputStream.java b/java/net/SocketOutputStream.java
new file mode 100644
index 0000000..9e8e792
--- /dev/null
+++ b/java/net/SocketOutputStream.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+
+import dalvik.system.BlockGuard;
+
+/**
+ * This stream extends FileOutputStream to implement a
+ * SocketOutputStream. Note that this class should <b>NOT</b> be
+ * public.
+ *
+ * @author Jonathan Payne
+ * @author Arthur van Hoff
+ */
+class SocketOutputStream extends FileOutputStream
+{
+ // Android-removed: Android doesn't need to call native init.
+ // static {
+ // init();
+ //}
+
+ private AbstractPlainSocketImpl impl = null;
+ private byte temp[] = new byte[1];
+ private Socket socket = null;
+
+ /**
+ * Creates a new SocketOutputStream. Can only be called
+ * by a Socket. This method needs to hang on to the owner Socket so
+ * that the fd will not be closed.
+ * @param impl the socket output stream inplemented
+ */
+ SocketOutputStream(AbstractPlainSocketImpl impl) throws IOException {
+ super(impl.getFileDescriptor());
+ this.impl = impl;
+ socket = impl.getSocket();
+ }
+
+ /**
+ * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
+ * object associated with this file output stream. </p>
+ *
+ * The {@code getChannel} method of {@code SocketOutputStream}
+ * returns {@code null} since it is a socket based stream.</p>
+ *
+ * @return the file channel associated with this file output stream
+ *
+ * @since 1.4
+ * @spec JSR-51
+ */
+ public final FileChannel getChannel() {
+ return null;
+ }
+
+ /**
+ * Writes to the socket.
+ * @param fd the FileDescriptor
+ * @param b the data to be written
+ * @param off the start offset in the data
+ * @param len the number of bytes that are written
+ * @exception IOException If an I/O error has occurred.
+ */
+ private native void socketWrite0(FileDescriptor fd, byte[] b, int off,
+ int len) throws IOException;
+
+ /**
+ * Writes to the socket with appropriate locking of the
+ * FileDescriptor.
+ * @param b the data to be written
+ * @param off the start offset in the data
+ * @param len the number of bytes that are written
+ * @exception IOException If an I/O error has occurred.
+ */
+ private void socketWrite(byte b[], int off, int len) throws IOException {
+
+
+ if (len <= 0 || off < 0 || len > b.length - off) {
+ if (len == 0) {
+ return;
+ }
+ throw new ArrayIndexOutOfBoundsException("len == " + len
+ + " off == " + off + " buffer length == " + b.length);
+ }
+
+ FileDescriptor fd = impl.acquireFD();
+ try {
+ // Android-added: Check BlockGuard policy in socketWrite.
+ BlockGuard.getThreadPolicy().onNetwork();
+ socketWrite0(fd, b, off, len);
+ } catch (SocketException se) {
+ if (se instanceof sun.net.ConnectionResetException) {
+ impl.setConnectionResetPending();
+ se = new SocketException("Connection reset");
+ }
+ if (impl.isClosedOrPending()) {
+ throw new SocketException("Socket closed");
+ } else {
+ throw se;
+ }
+ } finally {
+ impl.releaseFD();
+ }
+ }
+
+ /**
+ * Writes a byte to the socket.
+ * @param b the data to be written
+ * @exception IOException If an I/O error has occurred.
+ */
+ public void write(int b) throws IOException {
+ temp[0] = (byte)b;
+ socketWrite(temp, 0, 1);
+ }
+
+ /**
+ * Writes the contents of the buffer <i>b</i> to the socket.
+ * @param b the data to be written
+ * @exception SocketException If an I/O error has occurred.
+ */
+ public void write(byte b[]) throws IOException {
+ socketWrite(b, 0, b.length);
+ }
+
+ /**
+ * Writes <i>length</i> bytes from buffer <i>b</i> starting at
+ * offset <i>len</i>.
+ * @param b the data to be written
+ * @param off the start offset in the data
+ * @param len the number of bytes that are written
+ * @exception SocketException If an I/O error has occurred.
+ */
+ public void write(byte b[], int off, int len) throws IOException {
+ socketWrite(b, off, len);
+ }
+
+ /**
+ * Closes the stream.
+ */
+ private boolean closing = false;
+ public void close() throws IOException {
+ // Prevent recursion. See BugId 4484411
+ if (closing)
+ return;
+ closing = true;
+ if (socket != null) {
+ if (!socket.isClosed())
+ socket.close();
+ } else
+ impl.close();
+ closing = false;
+ }
+
+ /**
+ * Overrides finalize, the fd is closed by the Socket.
+ */
+ protected void finalize() {}
+
+ // Android-removed: Android doesn't need native init.
+ /*
+ * Perform class load-time initializations.
+ *
+ private native static void init();
+ */
+}
diff --git a/java/net/SocketPermission.java b/java/net/SocketPermission.java
new file mode 100644
index 0000000..2195ecc
--- /dev/null
+++ b/java/net/SocketPermission.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.security.Permission;
+
+
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
+/**
+ * Legacy security code; do not use.
+ */
+
+public final class SocketPermission extends Permission
+implements java.io.Serializable
+{
+
+ public SocketPermission(String host, String action) { super(""); }
+
+ public boolean implies(Permission p) { return true; }
+
+ public String getActions() { return null; }
+}
diff --git a/java/net/SocketSecrets.java b/java/net/SocketSecrets.java
new file mode 100644
index 0000000..9772906
--- /dev/null
+++ b/java/net/SocketSecrets.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+
+class SocketSecrets {
+
+ /* accessed by reflection from jdk.net.Sockets */
+
+ /* obj must be a Socket or ServerSocket */
+
+ private static <T> void setOption(Object obj, SocketOption<T> name, T value) throws IOException {
+ SocketImpl impl;
+
+ if (obj instanceof Socket) {
+ impl = ((Socket)obj).getImpl();
+ } else if (obj instanceof ServerSocket) {
+ impl = ((ServerSocket)obj).getImpl();
+ } else {
+ throw new IllegalArgumentException();
+ }
+ impl.setOption(name, value);
+ }
+
+ private static <T> T getOption(Object obj, SocketOption<T> name) throws IOException {
+ SocketImpl impl;
+
+ if (obj instanceof Socket) {
+ impl = ((Socket)obj).getImpl();
+ } else if (obj instanceof ServerSocket) {
+ impl = ((ServerSocket)obj).getImpl();
+ } else {
+ throw new IllegalArgumentException();
+ }
+ return impl.getOption(name);
+ }
+
+ private static <T> void setOption(DatagramSocket s, SocketOption<T> name, T value) throws IOException {
+ s.getImpl().setOption(name, value);
+ }
+
+ private static <T> T getOption(DatagramSocket s, SocketOption<T> name) throws IOException {
+ return s.getImpl().getOption(name);
+ }
+
+}
diff --git a/java/net/SocketTimeoutException.java b/java/net/SocketTimeoutException.java
new file mode 100644
index 0000000..5daf600
--- /dev/null
+++ b/java/net/SocketTimeoutException.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * Signals that a timeout has occurred on a socket read or accept.
+ *
+ * @since 1.4
+ */
+
+public class SocketTimeoutException extends java.io.InterruptedIOException {
+ private static final long serialVersionUID = -8846654841826352300L;
+
+ /**
+ * Constructs a new SocketTimeoutException with a detail
+ * message.
+ * @param msg the detail message
+ */
+ public SocketTimeoutException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Construct a new SocketTimeoutException with no detailed message.
+ */
+ public SocketTimeoutException() {}
+}
diff --git a/java/net/SocksConsts.java b/java/net/SocksConsts.java
new file mode 100644
index 0000000..df47aa7
--- /dev/null
+++ b/java/net/SocksConsts.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.net;
+
+/**
+ * Constants used by the SOCKS protocol implementation.
+ */
+
+interface SocksConsts {
+ static final int PROTO_VERS4 = 4;
+ static final int PROTO_VERS = 5;
+ static final int DEFAULT_PORT = 1080;
+
+ static final int NO_AUTH = 0;
+ static final int GSSAPI = 1;
+ static final int USER_PASSW = 2;
+ static final int NO_METHODS = -1;
+
+ static final int CONNECT = 1;
+ static final int BIND = 2;
+ static final int UDP_ASSOC = 3;
+
+ static final int IPV4 = 1;
+ static final int DOMAIN_NAME = 3;
+ static final int IPV6 = 4;
+
+ static final int REQUEST_OK = 0;
+ static final int GENERAL_FAILURE = 1;
+ static final int NOT_ALLOWED = 2;
+ static final int NET_UNREACHABLE = 3;
+ static final int HOST_UNREACHABLE = 4;
+ static final int CONN_REFUSED = 5;
+ static final int TTL_EXPIRED = 6;
+ static final int CMD_NOT_SUPPORTED = 7;
+ static final int ADDR_TYPE_NOT_SUP = 8;
+}
diff --git a/java/net/SocksSocketImpl.java b/java/net/SocksSocketImpl.java
new file mode 100644
index 0000000..0d9d8f5
--- /dev/null
+++ b/java/net/SocksSocketImpl.java
@@ -0,0 +1,1100 @@
+/*
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.net;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.BufferedOutputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import sun.net.SocksProxy;
+import sun.net.www.ParseUtil;
+/* import org.ietf.jgss.*; */
+
+/**
+ * SOCKS (V4 & V5) TCP socket implementation (RFC 1928).
+ * This is a subclass of PlainSocketImpl.
+ * Note this class should <b>NOT</b> be public.
+ */
+
+class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
+ private String server = null;
+ private int serverPort = DEFAULT_PORT;
+ private InetSocketAddress external_address;
+ private boolean useV4 = false;
+ private Socket cmdsock = null;
+ private InputStream cmdIn = null;
+ private OutputStream cmdOut = null;
+ /* true if the Proxy has been set programatically */
+ private boolean applicationSetProxy; /* false */
+
+
+ SocksSocketImpl() {
+ // Nothing needed
+ }
+
+ SocksSocketImpl(String server, int port) {
+ this.server = server;
+ this.serverPort = (port == -1 ? DEFAULT_PORT : port);
+ }
+
+ SocksSocketImpl(Proxy proxy) {
+ SocketAddress a = proxy.address();
+ if (a instanceof InetSocketAddress) {
+ InetSocketAddress ad = (InetSocketAddress) a;
+ // Use getHostString() to avoid reverse lookups
+ server = ad.getHostString();
+ serverPort = ad.getPort();
+ }
+ }
+
+ void setV4() {
+ useV4 = true;
+ }
+
+ private synchronized void privilegedConnect(final String host,
+ final int port,
+ final int timeout)
+ throws IOException
+ {
+ try {
+ AccessController.doPrivileged(
+ new java.security.PrivilegedExceptionAction<Void>() {
+ public Void run() throws IOException {
+ superConnectServer(host, port, timeout);
+ cmdIn = getInputStream();
+ cmdOut = getOutputStream();
+ return null;
+ }
+ });
+ } catch (java.security.PrivilegedActionException pae) {
+ throw (IOException) pae.getException();
+ }
+ }
+
+ private void superConnectServer(String host, int port,
+ int timeout) throws IOException {
+ super.connect(new InetSocketAddress(host, port), timeout);
+ }
+
+ private static int remainingMillis(long deadlineMillis) throws IOException {
+ if (deadlineMillis == 0L)
+ return 0;
+
+ final long remaining = deadlineMillis - System.currentTimeMillis();
+ if (remaining > 0)
+ return (int) remaining;
+
+ throw new SocketTimeoutException();
+ }
+
+ private int readSocksReply(InputStream in, byte[] data) throws IOException {
+ return readSocksReply(in, data, 0L);
+ }
+
+ private int readSocksReply(InputStream in, byte[] data, long deadlineMillis) throws IOException {
+ int len = data.length;
+ int received = 0;
+ for (int attempts = 0; received < len && attempts < 3; attempts++) {
+ int count;
+ try {
+ count = ((SocketInputStream)in).read(data, received, len - received, remainingMillis(deadlineMillis));
+ } catch (SocketTimeoutException e) {
+ throw new SocketTimeoutException("Connect timed out");
+ }
+ if (count < 0)
+ throw new SocketException("Malformed reply from SOCKS server");
+ received += count;
+ }
+ return received;
+ }
+
+ /**
+ * Provides the authentication machanism required by the proxy.
+ */
+ private boolean authenticate(byte method, InputStream in,
+ BufferedOutputStream out) throws IOException {
+ return authenticate(method, in, out, 0L);
+ }
+
+ private boolean authenticate(byte method, InputStream in,
+ BufferedOutputStream out,
+ long deadlineMillis) throws IOException {
+ // No Authentication required. We're done then!
+ if (method == NO_AUTH)
+ return true;
+ /**
+ * User/Password authentication. Try, in that order :
+ * - The application provided Authenticator, if any
+ * - the user.name & no password (backward compatibility behavior).
+ */
+ if (method == USER_PASSW) {
+ String userName;
+ String password = null;
+ final InetAddress addr = InetAddress.getByName(server);
+ PasswordAuthentication pw =
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<PasswordAuthentication>() {
+ public PasswordAuthentication run() {
+ return Authenticator.requestPasswordAuthentication(
+ server, addr, serverPort, "SOCKS5", "SOCKS authentication", null);
+ }
+ });
+ if (pw != null) {
+ userName = pw.getUserName();
+ password = new String(pw.getPassword());
+ } else {
+ userName = java.security.AccessController.doPrivileged(
+ new sun.security.action.GetPropertyAction("user.name"));
+ }
+ if (userName == null)
+ return false;
+ out.write(1);
+ out.write(userName.length());
+ try {
+ out.write(userName.getBytes("ISO-8859-1"));
+ } catch (java.io.UnsupportedEncodingException uee) {
+ assert false;
+ }
+ if (password != null) {
+ out.write(password.length());
+ try {
+ out.write(password.getBytes("ISO-8859-1"));
+ } catch (java.io.UnsupportedEncodingException uee) {
+ assert false;
+ }
+ } else
+ out.write(0);
+ out.flush();
+ byte[] data = new byte[2];
+ int i = readSocksReply(in, data, deadlineMillis);
+ if (i != 2 || data[1] != 0) {
+ /* RFC 1929 specifies that the connection MUST be closed if
+ authentication fails */
+ out.close();
+ in.close();
+ return false;
+ }
+ /* Authentication succeeded */
+ return true;
+ }
+ /**
+ * GSSAPI authentication mechanism.
+ * Unfortunately the RFC seems out of sync with the Reference
+ * implementation. I'll leave this in for future completion.
+ */
+// if (method == GSSAPI) {
+// try {
+// GSSManager manager = GSSManager.getInstance();
+// GSSName name = manager.createName("SERVICE:socks@"+server,
+// null);
+// GSSContext context = manager.createContext(name, null, null,
+// GSSContext.DEFAULT_LIFETIME);
+// context.requestMutualAuth(true);
+// context.requestReplayDet(true);
+// context.requestSequenceDet(true);
+// context.requestCredDeleg(true);
+// byte []inToken = new byte[0];
+// while (!context.isEstablished()) {
+// byte[] outToken
+// = context.initSecContext(inToken, 0, inToken.length);
+// // send the output token if generated
+// if (outToken != null) {
+// out.write(1);
+// out.write(1);
+// out.writeShort(outToken.length);
+// out.write(outToken);
+// out.flush();
+// data = new byte[2];
+// i = readSocksReply(in, data, deadlineMillis);
+// if (i != 2 || data[1] == 0xff) {
+// in.close();
+// out.close();
+// return false;
+// }
+// i = readSocksReply(in, data, deadlineMillis);
+// int len = 0;
+// len = ((int)data[0] & 0xff) << 8;
+// len += data[1];
+// data = new byte[len];
+// i = readSocksReply(in, data, deadlineMillis);
+// if (i == len)
+// return true;
+// in.close();
+// out.close();
+// }
+// }
+// } catch (GSSException e) {
+// /* RFC 1961 states that if Context initialisation fails the connection
+// MUST be closed */
+// e.printStackTrace();
+// in.close();
+// out.close();
+// }
+// }
+ return false;
+ }
+
+ private void connectV4(InputStream in, OutputStream out,
+ InetSocketAddress endpoint,
+ long deadlineMillis) throws IOException {
+ if (!(endpoint.getAddress() instanceof Inet4Address)) {
+ throw new SocketException("SOCKS V4 requires IPv4 only addresses");
+ }
+ out.write(PROTO_VERS4);
+ out.write(CONNECT);
+ out.write((endpoint.getPort() >> 8) & 0xff);
+ out.write((endpoint.getPort() >> 0) & 0xff);
+ out.write(endpoint.getAddress().getAddress());
+ String userName = getUserName();
+ try {
+ out.write(userName.getBytes("ISO-8859-1"));
+ } catch (java.io.UnsupportedEncodingException uee) {
+ assert false;
+ }
+ out.write(0);
+ out.flush();
+ byte[] data = new byte[8];
+ int n = readSocksReply(in, data, deadlineMillis);
+ if (n != 8)
+ throw new SocketException("Reply from SOCKS server has bad length: " + n);
+ if (data[0] != 0 && data[0] != 4)
+ throw new SocketException("Reply from SOCKS server has bad version");
+ SocketException ex = null;
+ switch (data[1]) {
+ case 90:
+ // Success!
+ external_address = endpoint;
+ break;
+ case 91:
+ ex = new SocketException("SOCKS request rejected");
+ break;
+ case 92:
+ ex = new SocketException("SOCKS server couldn't reach destination");
+ break;
+ case 93:
+ ex = new SocketException("SOCKS authentication failed");
+ break;
+ default:
+ ex = new SocketException("Reply from SOCKS server contains bad status");
+ break;
+ }
+ if (ex != null) {
+ in.close();
+ out.close();
+ throw ex;
+ }
+ }
+
+ /**
+ * Connects the Socks Socket to the specified endpoint. It will first
+ * connect to the SOCKS proxy and negotiate the access. If the proxy
+ * grants the connections, then the connect is successful and all
+ * further traffic will go to the "real" endpoint.
+ *
+ * @param endpoint the {@code SocketAddress} to connect to.
+ * @param timeout the timeout value in milliseconds
+ * @throws IOException if the connection can't be established.
+ * @throws SecurityException if there is a security manager and it
+ * doesn't allow the connection
+ * @throws IllegalArgumentException if endpoint is null or a
+ * SocketAddress subclass not supported by this socket
+ */
+ @Override
+ protected void connect(SocketAddress endpoint, int timeout) throws IOException {
+ final long deadlineMillis;
+
+ if (timeout == 0) {
+ deadlineMillis = 0L;
+ } else {
+ long finish = System.currentTimeMillis() + timeout;
+ deadlineMillis = finish < 0 ? Long.MAX_VALUE : finish;
+ }
+
+ SecurityManager security = System.getSecurityManager();
+ if (endpoint == null || !(endpoint instanceof InetSocketAddress))
+ throw new IllegalArgumentException("Unsupported address type");
+ InetSocketAddress epoint = (InetSocketAddress) endpoint;
+ if (security != null) {
+ if (epoint.isUnresolved())
+ security.checkConnect(epoint.getHostName(),
+ epoint.getPort());
+ else
+ security.checkConnect(epoint.getAddress().getHostAddress(),
+ epoint.getPort());
+ }
+ if (server == null) {
+ // Android-removed: Logic to establish proxy connection based on default ProxySelector.
+ // Removed code that tried to establish proxy connection if ProxySelector#getDefault()
+ // is not null. This was never the case in previous Android releases, was causing
+ // issues and therefore was removed.
+ /*
+ // This is the general case
+ // server is not null only when the socket was created with a
+ // specified proxy in which case it does bypass the ProxySelector
+ ProxySelector sel = java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<ProxySelector>() {
+ public ProxySelector run() {
+ return ProxySelector.getDefault();
+ }
+ });
+ if (sel == null) {
+ /*
+ * No default proxySelector --> direct connection
+ *
+ super.connect(epoint, remainingMillis(deadlineMillis));
+ return;
+ }
+ URI uri;
+ // Use getHostString() to avoid reverse lookups
+ String host = epoint.getHostString();
+ // IPv6 litteral?
+ if (epoint.getAddress() instanceof Inet6Address &&
+ (!host.startsWith("[")) && (host.indexOf(":") >= 0)) {
+ host = "[" + host + "]";
+ }
+ try {
+ uri = new URI("socket://" + ParseUtil.encodePath(host) + ":"+ epoint.getPort());
+ } catch (URISyntaxException e) {
+ // This shouldn't happen
+ assert false : e;
+ uri = null;
+ }
+ Proxy p = null;
+ IOException savedExc = null;
+ java.util.Iterator<Proxy> iProxy = null;
+ iProxy = sel.select(uri).iterator();
+ if (iProxy == null || !(iProxy.hasNext())) {
+ super.connect(epoint, remainingMillis(deadlineMillis));
+ return;
+ }
+ while (iProxy.hasNext()) {
+ p = iProxy.next();
+ if (p == null || p.type() != Proxy.Type.SOCKS) {
+ super.connect(epoint, remainingMillis(deadlineMillis));
+ return;
+ }
+
+ if (!(p.address() instanceof InetSocketAddress))
+ throw new SocketException("Unknown address type for proxy: " + p);
+ // Use getHostString() to avoid reverse lookups
+ server = ((InetSocketAddress) p.address()).getHostString();
+ serverPort = ((InetSocketAddress) p.address()).getPort();
+ if (p instanceof SocksProxy) {
+ if (((SocksProxy)p).protocolVersion() == 4) {
+ useV4 = true;
+ }
+ }
+
+ // Connects to the SOCKS server
+ try {
+ privilegedConnect(server, serverPort, remainingMillis(deadlineMillis));
+ // Worked, let's get outta here
+ break;
+ } catch (IOException e) {
+ // Ooops, let's notify the ProxySelector
+ sel.connectFailed(uri,p.address(),e);
+ server = null;
+ serverPort = -1;
+ savedExc = e;
+ // Will continue the while loop and try the next proxy
+ }
+ }
+
+ /*
+ * If server is still null at this point, none of the proxy
+ * worked
+ *
+ if (server == null) {
+ throw new SocketException("Can't connect to SOCKS proxy:"
+ + savedExc.getMessage());
+ }
+ */
+ super.connect(epoint, remainingMillis(deadlineMillis));
+ return;
+ } else {
+ // Connects to the SOCKS server
+ try {
+ privilegedConnect(server, serverPort, remainingMillis(deadlineMillis));
+ } catch (IOException e) {
+ throw new SocketException(e.getMessage());
+ }
+ }
+
+ // cmdIn & cmdOut were initialized during the privilegedConnect() call
+ BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512);
+ InputStream in = cmdIn;
+
+ if (useV4) {
+ // SOCKS Protocol version 4 doesn't know how to deal with
+ // DOMAIN type of addresses (unresolved addresses here)
+ if (epoint.isUnresolved())
+ throw new UnknownHostException(epoint.toString());
+ connectV4(in, out, epoint, deadlineMillis);
+ return;
+ }
+
+ // This is SOCKS V5
+ out.write(PROTO_VERS);
+ out.write(2);
+ out.write(NO_AUTH);
+ out.write(USER_PASSW);
+ out.flush();
+ byte[] data = new byte[2];
+ int i = readSocksReply(in, data, deadlineMillis);
+ if (i != 2 || ((int)data[0]) != PROTO_VERS) {
+ // Maybe it's not a V5 sever after all
+ // Let's try V4 before we give up
+ // SOCKS Protocol version 4 doesn't know how to deal with
+ // DOMAIN type of addresses (unresolved addresses here)
+ if (epoint.isUnresolved())
+ throw new UnknownHostException(epoint.toString());
+ connectV4(in, out, epoint, deadlineMillis);
+ return;
+ }
+ if (((int)data[1]) == NO_METHODS)
+ throw new SocketException("SOCKS : No acceptable methods");
+ if (!authenticate(data[1], in, out, deadlineMillis)) {
+ throw new SocketException("SOCKS : authentication failed");
+ }
+ out.write(PROTO_VERS);
+ out.write(CONNECT);
+ out.write(0);
+ /* Test for IPV4/IPV6/Unresolved */
+ if (epoint.isUnresolved()) {
+ out.write(DOMAIN_NAME);
+ out.write(epoint.getHostName().length());
+ try {
+ out.write(epoint.getHostName().getBytes("ISO-8859-1"));
+ } catch (java.io.UnsupportedEncodingException uee) {
+ assert false;
+ }
+ out.write((epoint.getPort() >> 8) & 0xff);
+ out.write((epoint.getPort() >> 0) & 0xff);
+ } else if (epoint.getAddress() instanceof Inet6Address) {
+ out.write(IPV6);
+ out.write(epoint.getAddress().getAddress());
+ out.write((epoint.getPort() >> 8) & 0xff);
+ out.write((epoint.getPort() >> 0) & 0xff);
+ } else {
+ out.write(IPV4);
+ out.write(epoint.getAddress().getAddress());
+ out.write((epoint.getPort() >> 8) & 0xff);
+ out.write((epoint.getPort() >> 0) & 0xff);
+ }
+ out.flush();
+ data = new byte[4];
+ i = readSocksReply(in, data, deadlineMillis);
+ if (i != 4)
+ throw new SocketException("Reply from SOCKS server has bad length");
+ SocketException ex = null;
+ int len;
+ byte[] addr;
+ switch (data[1]) {
+ case REQUEST_OK:
+ // success!
+ switch(data[3]) {
+ case IPV4:
+ addr = new byte[4];
+ i = readSocksReply(in, addr, deadlineMillis);
+ if (i != 4)
+ throw new SocketException("Reply from SOCKS server badly formatted");
+ data = new byte[2];
+ i = readSocksReply(in, data, deadlineMillis);
+ if (i != 2)
+ throw new SocketException("Reply from SOCKS server badly formatted");
+ break;
+ case DOMAIN_NAME:
+ len = data[1];
+ byte[] host = new byte[len];
+ i = readSocksReply(in, host, deadlineMillis);
+ if (i != len)
+ throw new SocketException("Reply from SOCKS server badly formatted");
+ data = new byte[2];
+ i = readSocksReply(in, data, deadlineMillis);
+ if (i != 2)
+ throw new SocketException("Reply from SOCKS server badly formatted");
+ break;
+ case IPV6:
+ len = data[1];
+ addr = new byte[len];
+ i = readSocksReply(in, addr, deadlineMillis);
+ if (i != len)
+ throw new SocketException("Reply from SOCKS server badly formatted");
+ data = new byte[2];
+ i = readSocksReply(in, data, deadlineMillis);
+ if (i != 2)
+ throw new SocketException("Reply from SOCKS server badly formatted");
+ break;
+ default:
+ ex = new SocketException("Reply from SOCKS server contains wrong code");
+ break;
+ }
+ break;
+ case GENERAL_FAILURE:
+ ex = new SocketException("SOCKS server general failure");
+ break;
+ case NOT_ALLOWED:
+ ex = new SocketException("SOCKS: Connection not allowed by ruleset");
+ break;
+ case NET_UNREACHABLE:
+ ex = new SocketException("SOCKS: Network unreachable");
+ break;
+ case HOST_UNREACHABLE:
+ ex = new SocketException("SOCKS: Host unreachable");
+ break;
+ case CONN_REFUSED:
+ ex = new SocketException("SOCKS: Connection refused");
+ break;
+ case TTL_EXPIRED:
+ ex = new SocketException("SOCKS: TTL expired");
+ break;
+ case CMD_NOT_SUPPORTED:
+ ex = new SocketException("SOCKS: Command not supported");
+ break;
+ case ADDR_TYPE_NOT_SUP:
+ ex = new SocketException("SOCKS: address type not supported");
+ break;
+ }
+ if (ex != null) {
+ in.close();
+ out.close();
+ throw ex;
+ }
+ external_address = epoint;
+ }
+
+ // Android-removed: Dead code. bindV4, socksBind, acceptFrom methods.
+ /*
+ private void bindV4(InputStream in, OutputStream out,
+ InetAddress baddr,
+ int lport) throws IOException {
+ if (!(baddr instanceof Inet4Address)) {
+ throw new SocketException("SOCKS V4 requires IPv4 only addresses");
+ }
+ super.bind(baddr, lport);
+ byte[] addr1 = baddr.getAddress();
+ /* Test for AnyLocal *
+ InetAddress naddr = baddr;
+ if (naddr.isAnyLocalAddress()) {
+ naddr = AccessController.doPrivileged(
+ new PrivilegedAction<InetAddress>() {
+ public InetAddress run() {
+ return cmdsock.getLocalAddress();
+
+ }
+ });
+ addr1 = naddr.getAddress();
+ }
+ out.write(PROTO_VERS4);
+ out.write(BIND);
+ out.write((super.getLocalPort() >> 8) & 0xff);
+ out.write((super.getLocalPort() >> 0) & 0xff);
+ out.write(addr1);
+ String userName = getUserName();
+ try {
+ out.write(userName.getBytes("ISO-8859-1"));
+ } catch (java.io.UnsupportedEncodingException uee) {
+ assert false;
+ }
+ out.write(0);
+ out.flush();
+ byte[] data = new byte[8];
+ int n = readSocksReply(in, data);
+ if (n != 8)
+ throw new SocketException("Reply from SOCKS server has bad length: " + n);
+ if (data[0] != 0 && data[0] != 4)
+ throw new SocketException("Reply from SOCKS server has bad version");
+ SocketException ex = null;
+ switch (data[1]) {
+ case 90:
+ // Success!
+ external_address = new InetSocketAddress(baddr, lport);
+ break;
+ case 91:
+ ex = new SocketException("SOCKS request rejected");
+ break;
+ case 92:
+ ex = new SocketException("SOCKS server couldn't reach destination");
+ break;
+ case 93:
+ ex = new SocketException("SOCKS authentication failed");
+ break;
+ default:
+ ex = new SocketException("Reply from SOCKS server contains bad status");
+ break;
+ }
+ if (ex != null) {
+ in.close();
+ out.close();
+ throw ex;
+ }
+
+ }
+
+ /**
+ * Sends the Bind request to the SOCKS proxy. In the SOCKS protocol, bind
+ * means "accept incoming connection from", so the SocketAddress is the
+ * the one of the host we do accept connection from.
+ *
+ * @param saddr the Socket address of the remote host.
+ * @exception IOException if an I/O error occurs when binding this socket.
+ *
+ protected synchronized void socksBind(InetSocketAddress saddr) throws IOException {
+ if (socket != null) {
+ // this is a client socket, not a server socket, don't
+ // call the SOCKS proxy for a bind!
+ return;
+ }
+
+ // Connects to the SOCKS server
+
+ if (server == null) {
+ // This is the general case
+ // server is not null only when the socket was created with a
+ // specified proxy in which case it does bypass the ProxySelector
+ ProxySelector sel = java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<ProxySelector>() {
+ public ProxySelector run() {
+ return ProxySelector.getDefault();
+ }
+ });
+ if (sel == null) {
+ /*
+ * No default proxySelector --> direct connection
+ *
+ return;
+ }
+ URI uri;
+ // Use getHostString() to avoid reverse lookups
+ String host = saddr.getHostString();
+ // IPv6 litteral?
+ if (saddr.getAddress() instanceof Inet6Address &&
+ (!host.startsWith("[")) && (host.indexOf(":") >= 0)) {
+ host = "[" + host + "]";
+ }
+ try {
+ uri = new URI("serversocket://" + ParseUtil.encodePath(host) + ":"+ saddr.getPort());
+ } catch (URISyntaxException e) {
+ // This shouldn't happen
+ assert false : e;
+ uri = null;
+ }
+ Proxy p = null;
+ Exception savedExc = null;
+ java.util.Iterator<Proxy> iProxy = null;
+ iProxy = sel.select(uri).iterator();
+ if (iProxy == null || !(iProxy.hasNext())) {
+ return;
+ }
+ while (iProxy.hasNext()) {
+ p = iProxy.next();
+ if (p == null || p.type() != Proxy.Type.SOCKS) {
+ return;
+ }
+
+ if (!(p.address() instanceof InetSocketAddress))
+ throw new SocketException("Unknown address type for proxy: " + p);
+ // Use getHostString() to avoid reverse lookups
+ server = ((InetSocketAddress) p.address()).getHostString();
+ serverPort = ((InetSocketAddress) p.address()).getPort();
+ if (p instanceof SocksProxy) {
+ if (((SocksProxy)p).protocolVersion() == 4) {
+ useV4 = true;
+ }
+ }
+
+ // Connects to the SOCKS server
+ try {
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Void>() {
+ public Void run() throws Exception {
+ cmdsock = new Socket(new PlainSocketImpl());
+ cmdsock.connect(new InetSocketAddress(server, serverPort));
+ cmdIn = cmdsock.getInputStream();
+ cmdOut = cmdsock.getOutputStream();
+ return null;
+ }
+ });
+ } catch (Exception e) {
+ // Ooops, let's notify the ProxySelector
+ sel.connectFailed(uri,p.address(),new SocketException(e.getMessage()));
+ server = null;
+ serverPort = -1;
+ cmdsock = null;
+ savedExc = e;
+ // Will continue the while loop and try the next proxy
+ }
+ }
+
+ /*
+ * If server is still null at this point, none of the proxy
+ * worked
+ *
+ if (server == null || cmdsock == null) {
+ throw new SocketException("Can't connect to SOCKS proxy:"
+ + savedExc.getMessage());
+ }
+ } else {
+ try {
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Void>() {
+ public Void run() throws Exception {
+ cmdsock = new Socket(new PlainSocketImpl());
+ cmdsock.connect(new InetSocketAddress(server, serverPort));
+ cmdIn = cmdsock.getInputStream();
+ cmdOut = cmdsock.getOutputStream();
+ return null;
+ }
+ });
+ } catch (Exception e) {
+ throw new SocketException(e.getMessage());
+ }
+ }
+ BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512);
+ InputStream in = cmdIn;
+ if (useV4) {
+ bindV4(in, out, saddr.getAddress(), saddr.getPort());
+ return;
+ }
+ out.write(PROTO_VERS);
+ out.write(2);
+ out.write(NO_AUTH);
+ out.write(USER_PASSW);
+ out.flush();
+ byte[] data = new byte[2];
+ int i = readSocksReply(in, data);
+ if (i != 2 || ((int)data[0]) != PROTO_VERS) {
+ // Maybe it's not a V5 sever after all
+ // Let's try V4 before we give up
+ bindV4(in, out, saddr.getAddress(), saddr.getPort());
+ return;
+ }
+ if (((int)data[1]) == NO_METHODS)
+ throw new SocketException("SOCKS : No acceptable methods");
+ if (!authenticate(data[1], in, out)) {
+ throw new SocketException("SOCKS : authentication failed");
+ }
+ // We're OK. Let's issue the BIND command.
+ out.write(PROTO_VERS);
+ out.write(BIND);
+ out.write(0);
+ int lport = saddr.getPort();
+ if (saddr.isUnresolved()) {
+ out.write(DOMAIN_NAME);
+ out.write(saddr.getHostName().length());
+ try {
+ out.write(saddr.getHostName().getBytes("ISO-8859-1"));
+ } catch (java.io.UnsupportedEncodingException uee) {
+ assert false;
+ }
+ out.write((lport >> 8) & 0xff);
+ out.write((lport >> 0) & 0xff);
+ } else if (saddr.getAddress() instanceof Inet4Address) {
+ byte[] addr1 = saddr.getAddress().getAddress();
+ out.write(IPV4);
+ out.write(addr1);
+ out.write((lport >> 8) & 0xff);
+ out.write((lport >> 0) & 0xff);
+ out.flush();
+ } else if (saddr.getAddress() instanceof Inet6Address) {
+ byte[] addr1 = saddr.getAddress().getAddress();
+ out.write(IPV6);
+ out.write(addr1);
+ out.write((lport >> 8) & 0xff);
+ out.write((lport >> 0) & 0xff);
+ out.flush();
+ } else {
+ cmdsock.close();
+ throw new SocketException("unsupported address type : " + saddr);
+ }
+ data = new byte[4];
+ i = readSocksReply(in, data);
+ SocketException ex = null;
+ int len, nport;
+ byte[] addr;
+ switch (data[1]) {
+ case REQUEST_OK:
+ // success!
+ switch(data[3]) {
+ case IPV4:
+ addr = new byte[4];
+ i = readSocksReply(in, addr);
+ if (i != 4)
+ throw new SocketException("Reply from SOCKS server badly formatted");
+ data = new byte[2];
+ i = readSocksReply(in, data);
+ if (i != 2)
+ throw new SocketException("Reply from SOCKS server badly formatted");
+ nport = ((int)data[0] & 0xff) << 8;
+ nport += ((int)data[1] & 0xff);
+ external_address =
+ new InetSocketAddress(new Inet4Address("", addr) , nport);
+ break;
+ case DOMAIN_NAME:
+ len = data[1];
+ byte[] host = new byte[len];
+ i = readSocksReply(in, host);
+ if (i != len)
+ throw new SocketException("Reply from SOCKS server badly formatted");
+ data = new byte[2];
+ i = readSocksReply(in, data);
+ if (i != 2)
+ throw new SocketException("Reply from SOCKS server badly formatted");
+ nport = ((int)data[0] & 0xff) << 8;
+ nport += ((int)data[1] & 0xff);
+ external_address = new InetSocketAddress(new String(host), nport);
+ break;
+ case IPV6:
+ len = data[1];
+ addr = new byte[len];
+ i = readSocksReply(in, addr);
+ if (i != len)
+ throw new SocketException("Reply from SOCKS server badly formatted");
+ data = new byte[2];
+ i = readSocksReply(in, data);
+ if (i != 2)
+ throw new SocketException("Reply from SOCKS server badly formatted");
+ nport = ((int)data[0] & 0xff) << 8;
+ nport += ((int)data[1] & 0xff);
+ external_address =
+ new InetSocketAddress(new Inet6Address("", addr), nport);
+ break;
+ }
+ break;
+ case GENERAL_FAILURE:
+ ex = new SocketException("SOCKS server general failure");
+ break;
+ case NOT_ALLOWED:
+ ex = new SocketException("SOCKS: Bind not allowed by ruleset");
+ break;
+ case NET_UNREACHABLE:
+ ex = new SocketException("SOCKS: Network unreachable");
+ break;
+ case HOST_UNREACHABLE:
+ ex = new SocketException("SOCKS: Host unreachable");
+ break;
+ case CONN_REFUSED:
+ ex = new SocketException("SOCKS: Connection refused");
+ break;
+ case TTL_EXPIRED:
+ ex = new SocketException("SOCKS: TTL expired");
+ break;
+ case CMD_NOT_SUPPORTED:
+ ex = new SocketException("SOCKS: Command not supported");
+ break;
+ case ADDR_TYPE_NOT_SUP:
+ ex = new SocketException("SOCKS: address type not supported");
+ break;
+ }
+ if (ex != null) {
+ in.close();
+ out.close();
+ cmdsock.close();
+ cmdsock = null;
+ throw ex;
+ }
+ cmdIn = in;
+ cmdOut = out;
+ }
+
+ /**
+ * Accepts a connection from a specific host.
+ *
+ * @param s the accepted connection.
+ * @param saddr the socket address of the host we do accept
+ * connection from
+ * @exception IOException if an I/O error occurs when accepting the
+ * connection.
+ *
+ protected void acceptFrom(SocketImpl s, InetSocketAddress saddr) throws IOException {
+ if (cmdsock == null) {
+ // Not a Socks ServerSocket.
+ return;
+ }
+ InputStream in = cmdIn;
+ // Sends the "SOCKS BIND" request.
+ socksBind(saddr);
+ in.read();
+ int i = in.read();
+ in.read();
+ SocketException ex = null;
+ int nport;
+ byte[] addr;
+ InetSocketAddress real_end = null;
+ switch (i) {
+ case REQUEST_OK:
+ // success!
+ i = in.read();
+ switch(i) {
+ case IPV4:
+ addr = new byte[4];
+ readSocksReply(in, addr);
+ nport = in.read() << 8;
+ nport += in.read();
+ real_end =
+ new InetSocketAddress(new Inet4Address("", addr) , nport);
+ break;
+ case DOMAIN_NAME:
+ int len = in.read();
+ addr = new byte[len];
+ readSocksReply(in, addr);
+ nport = in.read() << 8;
+ nport += in.read();
+ real_end = new InetSocketAddress(new String(addr), nport);
+ break;
+ case IPV6:
+ addr = new byte[16];
+ readSocksReply(in, addr);
+ nport = in.read() << 8;
+ nport += in.read();
+ real_end =
+ new InetSocketAddress(new Inet6Address("", addr), nport);
+ break;
+ }
+ break;
+ case GENERAL_FAILURE:
+ ex = new SocketException("SOCKS server general failure");
+ break;
+ case NOT_ALLOWED:
+ ex = new SocketException("SOCKS: Accept not allowed by ruleset");
+ break;
+ case NET_UNREACHABLE:
+ ex = new SocketException("SOCKS: Network unreachable");
+ break;
+ case HOST_UNREACHABLE:
+ ex = new SocketException("SOCKS: Host unreachable");
+ break;
+ case CONN_REFUSED:
+ ex = new SocketException("SOCKS: Connection refused");
+ break;
+ case TTL_EXPIRED:
+ ex = new SocketException("SOCKS: TTL expired");
+ break;
+ case CMD_NOT_SUPPORTED:
+ ex = new SocketException("SOCKS: Command not supported");
+ break;
+ case ADDR_TYPE_NOT_SUP:
+ ex = new SocketException("SOCKS: address type not supported");
+ break;
+ }
+ if (ex != null) {
+ cmdIn.close();
+ cmdOut.close();
+ cmdsock.close();
+ cmdsock = null;
+ throw ex;
+ }
+
+ /**
+ * This is where we have to do some fancy stuff.
+ * The datastream from the socket "accepted" by the proxy will
+ * come through the cmdSocket. So we have to swap the socketImpls
+ *
+ if (s instanceof SocksSocketImpl) {
+ ((SocksSocketImpl)s).external_address = real_end;
+ }
+ if (s instanceof PlainSocketImpl) {
+ PlainSocketImpl psi = (PlainSocketImpl) s;
+ psi.setInputStream((SocketInputStream) in);
+ psi.setFileDescriptor(cmdsock.getImpl().getFileDescriptor());
+ psi.setAddress(cmdsock.getImpl().getInetAddress());
+ psi.setPort(cmdsock.getImpl().getPort());
+ psi.setLocalPort(cmdsock.getImpl().getLocalPort());
+ } else {
+ s.fd = cmdsock.getImpl().fd;
+ s.address = cmdsock.getImpl().address;
+ s.port = cmdsock.getImpl().port;
+ s.localport = cmdsock.getImpl().localport;
+ }
+
+ // Need to do that so that the socket won't be closed
+ // when the ServerSocket is closed by the user.
+ // It kinds of detaches the Socket because it is now
+ // used elsewhere.
+ cmdsock = null;
+ }
+ */
+
+ /**
+ * Returns the value of this socket's {@code address} field.
+ *
+ * @return the value of this socket's {@code address} field.
+ * @see java.net.SocketImpl#address
+ */
+ @Override
+ protected InetAddress getInetAddress() {
+ if (external_address != null)
+ return external_address.getAddress();
+ else
+ return super.getInetAddress();
+ }
+
+ /**
+ * Returns the value of this socket's {@code port} field.
+ *
+ * @return the value of this socket's {@code port} field.
+ * @see java.net.SocketImpl#port
+ */
+ @Override
+ protected int getPort() {
+ if (external_address != null)
+ return external_address.getPort();
+ else
+ return super.getPort();
+ }
+
+ @Override
+ protected int getLocalPort() {
+ if (socket != null)
+ return super.getLocalPort();
+ if (external_address != null)
+ return external_address.getPort();
+ else
+ return super.getLocalPort();
+ }
+
+ @Override
+ protected void close() throws IOException {
+ if (cmdsock != null)
+ cmdsock.close();
+ cmdsock = null;
+ super.close();
+ }
+
+ private String getUserName() {
+ String userName = "";
+ if (applicationSetProxy) {
+ try {
+ userName = System.getProperty("user.name");
+ } catch (SecurityException se) { /* swallow Exception */ }
+ } else {
+ userName = java.security.AccessController.doPrivileged(
+ new sun.security.action.GetPropertyAction("user.name"));
+ }
+ return userName;
+ }
+}
diff --git a/java/net/StandardProtocolFamily.java b/java/net/StandardProtocolFamily.java
new file mode 100644
index 0000000..25aaed4
--- /dev/null
+++ b/java/net/StandardProtocolFamily.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2007, 2009, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * Defines the standard families of communication protocols.
+ *
+ * @since 1.7
+ */
+
+public enum StandardProtocolFamily implements ProtocolFamily {
+
+ /**
+ * Internet Protocol Version 4 (IPv4)
+ */
+ INET,
+
+ /**
+ * Internet Protocol Version 6 (IPv6)
+ */
+ INET6
+}
diff --git a/java/net/StandardSocketOptions.java b/java/net/StandardSocketOptions.java
new file mode 100644
index 0000000..7fdd5f0
--- /dev/null
+++ b/java/net/StandardSocketOptions.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * Defines the <em>standard</em> socket options.
+ *
+ * <p> The {@link SocketOption#name name} of each socket option defined by this
+ * class is its field name.
+ *
+ * <p> In this release, the socket options defined here are used by {@link
+ * java.nio.channels.NetworkChannel network} channels in the {@link
+ * java.nio.channels channels} package.
+ *
+ * @since 1.7
+ */
+
+public final class StandardSocketOptions {
+ private StandardSocketOptions() { }
+
+ // -- SOL_SOCKET --
+
+ /**
+ * Allow transmission of broadcast datagrams.
+ *
+ * <p> The value of this socket option is a {@code Boolean} that represents
+ * whether the option is enabled or disabled. The option is specific to
+ * datagram-oriented sockets sending to {@link java.net.Inet4Address IPv4}
+ * broadcast addresses. When the socket option is enabled then the socket
+ * can be used to send <em>broadcast datagrams</em>.
+ *
+ * <p> The initial value of this socket option is {@code FALSE}. The socket
+ * option may be enabled or disabled at any time. Some operating systems may
+ * require that the Java virtual machine be started with implementation
+ * specific privileges to enable this option or send broadcast datagrams.
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc919.txt">RFC 929:
+ * Broadcasting Internet Datagrams</a>
+ * @see DatagramSocket#setBroadcast
+ */
+ public static final SocketOption<Boolean> SO_BROADCAST =
+ new StdSocketOption<Boolean>("SO_BROADCAST", Boolean.class);
+
+ /**
+ * Keep connection alive.
+ *
+ * <p> The value of this socket option is a {@code Boolean} that represents
+ * whether the option is enabled or disabled. When the {@code SO_KEEPALIVE}
+ * option is enabled the operating system may use a <em>keep-alive</em>
+ * mechanism to periodically probe the other end of a connection when the
+ * connection is otherwise idle. The exact semantics of the keep alive
+ * mechanism is system dependent and therefore unspecified.
+ *
+ * <p> The initial value of this socket option is {@code FALSE}. The socket
+ * option may be enabled or disabled at any time.
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122
+ * Requirements for Internet Hosts -- Communication Layers</a>
+ * @see Socket#setKeepAlive
+ */
+ public static final SocketOption<Boolean> SO_KEEPALIVE =
+ new StdSocketOption<Boolean>("SO_KEEPALIVE", Boolean.class);
+
+ /**
+ * The size of the socket send buffer.
+ *
+ * <p> The value of this socket option is an {@code Integer} that is the
+ * size of the socket send buffer in bytes. The socket send buffer is an
+ * output buffer used by the networking implementation. It may need to be
+ * increased for high-volume connections. The value of the socket option is
+ * a <em>hint</em> to the implementation to size the buffer and the actual
+ * size may differ. The socket option can be queried to retrieve the actual
+ * size.
+ *
+ * <p> For datagram-oriented sockets, the size of the send buffer may limit
+ * the size of the datagrams that may be sent by the socket. Whether
+ * datagrams larger than the buffer size are sent or discarded is system
+ * dependent.
+ *
+ * <p> The initial/default size of the socket send buffer and the range of
+ * allowable values is system dependent although a negative size is not
+ * allowed. An attempt to set the socket send buffer to larger than its
+ * maximum size causes it to be set to its maximum size.
+ *
+ * <p> An implementation allows this socket option to be set before the
+ * socket is bound or connected. Whether an implementation allows the
+ * socket send buffer to be changed after the socket is bound is system
+ * dependent.
+ *
+ * @see Socket#setSendBufferSize
+ */
+ public static final SocketOption<Integer> SO_SNDBUF =
+ new StdSocketOption<Integer>("SO_SNDBUF", Integer.class);
+
+
+ /**
+ * The size of the socket receive buffer.
+ *
+ * <p> The value of this socket option is an {@code Integer} that is the
+ * size of the socket receive buffer in bytes. The socket receive buffer is
+ * an input buffer used by the networking implementation. It may need to be
+ * increased for high-volume connections or decreased to limit the possible
+ * backlog of incoming data. The value of the socket option is a
+ * <em>hint</em> to the implementation to size the buffer and the actual
+ * size may differ.
+ *
+ * <p> For datagram-oriented sockets, the size of the receive buffer may
+ * limit the size of the datagrams that can be received. Whether datagrams
+ * larger than the buffer size can be received is system dependent.
+ * Increasing the socket receive buffer may be important for cases where
+ * datagrams arrive in bursts faster than they can be processed.
+ *
+ * <p> In the case of stream-oriented sockets and the TCP/IP protocol, the
+ * size of the socket receive buffer may be used when advertising the size
+ * of the TCP receive window to the remote peer.
+ *
+ * <p> The initial/default size of the socket receive buffer and the range
+ * of allowable values is system dependent although a negative size is not
+ * allowed. An attempt to set the socket receive buffer to larger than its
+ * maximum size causes it to be set to its maximum size.
+ *
+ * <p> An implementation allows this socket option to be set before the
+ * socket is bound or connected. Whether an implementation allows the
+ * socket receive buffer to be changed after the socket is bound is system
+ * dependent.
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc1323.txt">RFC 1323: TCP
+ * Extensions for High Performance</a>
+ * @see Socket#setReceiveBufferSize
+ * @see ServerSocket#setReceiveBufferSize
+ */
+ public static final SocketOption<Integer> SO_RCVBUF =
+ new StdSocketOption<Integer>("SO_RCVBUF", Integer.class);
+
+ /**
+ * Re-use address.
+ *
+ * <p> The value of this socket option is a {@code Boolean} that represents
+ * whether the option is enabled or disabled. The exact semantics of this
+ * socket option are socket type and system dependent.
+ *
+ * <p> In the case of stream-oriented sockets, this socket option will
+ * usually determine whether the socket can be bound to a socket address
+ * when a previous connection involving that socket address is in the
+ * <em>TIME_WAIT</em> state. On implementations where the semantics differ,
+ * and the socket option is not required to be enabled in order to bind the
+ * socket when a previous connection is in this state, then the
+ * implementation may choose to ignore this option.
+ *
+ * <p> For datagram-oriented sockets the socket option is used to allow
+ * multiple programs bind to the same address. This option should be enabled
+ * when the socket is to be used for Internet Protocol (IP) multicasting.
+ *
+ * <p> An implementation allows this socket option to be set before the
+ * socket is bound or connected. Changing the value of this socket option
+ * after the socket is bound has no effect. The default value of this
+ * socket option is system dependent.
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc793.txt">RFC 793: Transmission
+ * Control Protocol</a>
+ * @see ServerSocket#setReuseAddress
+ */
+ public static final SocketOption<Boolean> SO_REUSEADDR =
+ new StdSocketOption<Boolean>("SO_REUSEADDR", Boolean.class);
+
+ /**
+ * Linger on close if data is present.
+ *
+ * <p> The value of this socket option is an {@code Integer} that controls
+ * the action taken when unsent data is queued on the socket and a method
+ * to close the socket is invoked. If the value of the socket option is zero
+ * or greater, then it represents a timeout value, in seconds, known as the
+ * <em>linger interval</em>. The linger interval is the timeout for the
+ * {@code close} method to block while the operating system attempts to
+ * transmit the unsent data or it decides that it is unable to transmit the
+ * data. If the value of the socket option is less than zero then the option
+ * is disabled. In that case the {@code close} method does not wait until
+ * unsent data is transmitted; if possible the operating system will transmit
+ * any unsent data before the connection is closed.
+ *
+ * <p> This socket option is intended for use with sockets that are configured
+ * in {@link java.nio.channels.SelectableChannel#isBlocking() blocking} mode
+ * only. The behavior of the {@code close} method when this option is
+ * enabled on a non-blocking socket is not defined.
+ *
+ * <p> The initial value of this socket option is a negative value, meaning
+ * that the option is disabled. The option may be enabled, or the linger
+ * interval changed, at any time. The maximum value of the linger interval
+ * is system dependent. Setting the linger interval to a value that is
+ * greater than its maximum value causes the linger interval to be set to
+ * its maximum value.
+ *
+ * @see Socket#setSoLinger
+ */
+ public static final SocketOption<Integer> SO_LINGER =
+ new StdSocketOption<Integer>("SO_LINGER", Integer.class);
+
+
+ // -- IPPROTO_IP --
+
+ /**
+ * The Type of Service (ToS) octet in the Internet Protocol (IP) header.
+ *
+ * <p> The value of this socket option is an {@code Integer} representing
+ * the value of the ToS octet in IP packets sent by sockets to an {@link
+ * StandardProtocolFamily#INET IPv4} socket. The interpretation of the ToS
+ * octet is network specific and is not defined by this class. Further
+ * information on the ToS octet can be found in <a
+ * href="http://www.ietf.org/rfc/rfc1349.txt">RFC 1349</a> and <a
+ * href="http://www.ietf.org/rfc/rfc2474.txt">RFC 2474</a>. The value
+ * of the socket option is a <em>hint</em>. An implementation may ignore the
+ * value, or ignore specific values.
+ *
+ * <p> The initial/default value of the TOS field in the ToS octet is
+ * implementation specific but will typically be {@code 0}. For
+ * datagram-oriented sockets the option may be configured at any time after
+ * the socket has been bound. The new value of the octet is used when sending
+ * subsequent datagrams. It is system dependent whether this option can be
+ * queried or changed prior to binding the socket.
+ *
+ * <p> The behavior of this socket option on a stream-oriented socket, or an
+ * {@link StandardProtocolFamily#INET6 IPv6} socket, is not defined in this
+ * release.
+ *
+ * @see DatagramSocket#setTrafficClass
+ */
+ public static final SocketOption<Integer> IP_TOS =
+ new StdSocketOption<Integer>("IP_TOS", Integer.class);
+
+ /**
+ * The network interface for Internet Protocol (IP) multicast datagrams.
+ *
+ * <p> The value of this socket option is a {@link NetworkInterface} that
+ * represents the outgoing interface for multicast datagrams sent by the
+ * datagram-oriented socket. For {@link StandardProtocolFamily#INET6 IPv6}
+ * sockets then it is system dependent whether setting this option also
+ * sets the outgoing interface for multicast datagrams sent to IPv4
+ * addresses.
+ *
+ * <p> The initial/default value of this socket option may be {@code null}
+ * to indicate that outgoing interface will be selected by the operating
+ * system, typically based on the network routing tables. An implementation
+ * allows this socket option to be set after the socket is bound. Whether
+ * the socket option can be queried or changed prior to binding the socket
+ * is system dependent.
+ *
+ * @see java.nio.channels.MulticastChannel
+ * @see MulticastSocket#setInterface
+ */
+ public static final SocketOption<NetworkInterface> IP_MULTICAST_IF =
+ new StdSocketOption<NetworkInterface>("IP_MULTICAST_IF", NetworkInterface.class);
+
+ /**
+ * The <em>time-to-live</em> for Internet Protocol (IP) multicast datagrams.
+ *
+ * <p> The value of this socket option is an {@code Integer} in the range
+ * {@code 0 <= value <= 255}. It is used to control the scope of multicast
+ * datagrams sent by the datagram-oriented socket.
+ * In the case of an {@link StandardProtocolFamily#INET IPv4} socket
+ * the option is the time-to-live (TTL) on multicast datagrams sent by the
+ * socket. Datagrams with a TTL of zero are not transmitted on the network
+ * but may be delivered locally. In the case of an {@link
+ * StandardProtocolFamily#INET6 IPv6} socket the option is the
+ * <em>hop limit</em> which is number of <em>hops</em> that the datagram can
+ * pass through before expiring on the network. For IPv6 sockets it is
+ * system dependent whether the option also sets the <em>time-to-live</em>
+ * on multicast datagrams sent to IPv4 addresses.
+ *
+ * <p> The initial/default value of the time-to-live setting is typically
+ * {@code 1}. An implementation allows this socket option to be set after
+ * the socket is bound. Whether the socket option can be queried or changed
+ * prior to binding the socket is system dependent.
+ *
+ * @see java.nio.channels.MulticastChannel
+ * @see MulticastSocket#setTimeToLive
+ */
+ public static final SocketOption<Integer> IP_MULTICAST_TTL =
+ new StdSocketOption<Integer>("IP_MULTICAST_TTL", Integer.class);
+
+ /**
+ * Loopback for Internet Protocol (IP) multicast datagrams.
+ *
+ * <p> The value of this socket option is a {@code Boolean} that controls
+ * the <em>loopback</em> of multicast datagrams. The value of the socket
+ * option represents if the option is enabled or disabled.
+ *
+ * <p> The exact semantics of this socket options are system dependent.
+ * In particular, it is system dependent whether the loopback applies to
+ * multicast datagrams sent from the socket or received by the socket.
+ * For {@link StandardProtocolFamily#INET6 IPv6} sockets then it is
+ * system dependent whether the option also applies to multicast datagrams
+ * sent to IPv4 addresses.
+ *
+ * <p> The initial/default value of this socket option is {@code TRUE}. An
+ * implementation allows this socket option to be set after the socket is
+ * bound. Whether the socket option can be queried or changed prior to
+ * binding the socket is system dependent.
+ *
+ * @see java.nio.channels.MulticastChannel
+ * @see MulticastSocket#setLoopbackMode
+ */
+ public static final SocketOption<Boolean> IP_MULTICAST_LOOP =
+ new StdSocketOption<Boolean>("IP_MULTICAST_LOOP", Boolean.class);
+
+
+ // -- IPPROTO_TCP --
+
+ /**
+ * Disable the Nagle algorithm.
+ *
+ * <p> The value of this socket option is a {@code Boolean} that represents
+ * whether the option is enabled or disabled. The socket option is specific to
+ * stream-oriented sockets using the TCP/IP protocol. TCP/IP uses an algorithm
+ * known as <em>The Nagle Algorithm</em> to coalesce short segments and
+ * improve network efficiency.
+ *
+ * <p> The default value of this socket option is {@code FALSE}. The
+ * socket option should only be enabled in cases where it is known that the
+ * coalescing impacts performance. The socket option may be enabled at any
+ * time. In other words, the Nagle Algorithm can be disabled. Once the option
+ * is enabled, it is system dependent whether it can be subsequently
+ * disabled. If it cannot, then invoking the {@code setOption} method to
+ * disable the option has no effect.
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122:
+ * Requirements for Internet Hosts -- Communication Layers</a>
+ * @see Socket#setTcpNoDelay
+ */
+ public static final SocketOption<Boolean> TCP_NODELAY =
+ new StdSocketOption<Boolean>("TCP_NODELAY", Boolean.class);
+
+
+ private static class StdSocketOption<T> implements SocketOption<T> {
+ private final String name;
+ private final Class<T> type;
+ StdSocketOption(String name, Class<T> type) {
+ this.name = name;
+ this.type = type;
+ }
+ @Override public String name() { return name; }
+ @Override public Class<T> type() { return type; }
+ @Override public String toString() { return name; }
+ }
+}
diff --git a/java/net/URI.java b/java/net/URI.java
new file mode 100644
index 0000000..ebba41b
--- /dev/null
+++ b/java/net/URI.java
@@ -0,0 +1,3602 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.CharacterCodingException;
+import java.text.Normalizer;
+import sun.nio.cs.ThreadLocalCoders;
+
+import java.lang.Character; // for javadoc
+import java.lang.NullPointerException; // for javadoc
+
+
+// Android-changed: Reformat @see links.
+/**
+ * Represents a Uniform Resource Identifier (URI) reference.
+ *
+ * <p> Aside from some minor deviations noted below, an instance of this
+ * class represents a URI reference as defined by
+ * <a href="http://www.ietf.org/rfc/rfc2396.txt"><i>RFC 2396: Uniform
+ * Resource Identifiers (URI): Generic Syntax</i></a>, amended by <a
+ * href="http://www.ietf.org/rfc/rfc2732.txt"><i>RFC 2732: Format for
+ * Literal IPv6 Addresses in URLs</i></a>. The Literal IPv6 address format
+ * also supports scope_ids. The syntax and usage of scope_ids is described
+ * <a href="Inet6Address.html#scoped">here</a>.
+ * This class provides constructors for creating URI instances from
+ * their components or by parsing their string forms, methods for accessing the
+ * various components of an instance, and methods for normalizing, resolving,
+ * and relativizing URI instances. Instances of this class are immutable.
+ *
+ *
+ * <h3> URI syntax and components </h3>
+ *
+ * At the highest level a URI reference (hereinafter simply "URI") in string
+ * form has the syntax
+ *
+ * <blockquote>
+ * [<i>scheme</i><b>{@code :}</b>]<i>scheme-specific-part</i>[<b>{@code #}</b><i>fragment</i>]
+ * </blockquote>
+ *
+ * where square brackets [...] delineate optional components and the characters
+ * <b>{@code :}</b> and <b>{@code #}</b> stand for themselves.
+ *
+ * <p> An <i>absolute</i> URI specifies a scheme; a URI that is not absolute is
+ * said to be <i>relative</i>. URIs are also classified according to whether
+ * they are <i>opaque</i> or <i>hierarchical</i>.
+ *
+ * <p> An <i>opaque</i> URI is an absolute URI whose scheme-specific part does
+ * not begin with a slash character ({@code '/'}). Opaque URIs are not
+ * subject to further parsing. Some examples of opaque URIs are:
+ *
+ * <blockquote><table cellpadding=0 cellspacing=0 summary="layout">
+ * <tr><td>{@code mailto:[email protected]}<td></tr>
+ * <tr><td>{@code news:comp.lang.java}<td></tr>
+ * <tr><td>{@code urn:isbn:096139210x}</td></tr>
+ * </table></blockquote>
+ *
+ * <p> A <i>hierarchical</i> URI is either an absolute URI whose
+ * scheme-specific part begins with a slash character, or a relative URI, that
+ * is, a URI that does not specify a scheme. Some examples of hierarchical
+ * URIs are:
+ *
+ * <blockquote>
+ * {@code http://java.sun.com/j2se/1.3/}<br>
+ * {@code docs/guide/collections/designfaq.html#28}<br>
+ * {@code ../../../demo/jfc/SwingSet2/src/SwingSet2.java}<br>
+ * {@code file:///~/calendar}
+ * </blockquote>
+ *
+ * <p> A hierarchical URI is subject to further parsing according to the syntax
+ *
+ * <blockquote>
+ * [<i>scheme</i><b>{@code :}</b>][<b>{@code //}</b><i>authority</i>][<i>path</i>][<b>{@code ?}</b><i>query</i>][<b>{@code #}</b><i>fragment</i>]
+ * </blockquote>
+ *
+ * where the characters <b>{@code :}</b>, <b>{@code /}</b>,
+ * <b>{@code ?}</b>, and <b>{@code #}</b> stand for themselves. The
+ * scheme-specific part of a hierarchical URI consists of the characters
+ * between the scheme and fragment components.
+ *
+ * <p> The authority component of a hierarchical URI is, if specified, either
+ * <i>server-based</i> or <i>registry-based</i>. A server-based authority
+ * parses according to the familiar syntax
+ *
+ * <blockquote>
+ * [<i>user-info</i><b>{@code @}</b>]<i>host</i>[<b>{@code :}</b><i>port</i>]
+ * </blockquote>
+ *
+ * where the characters <b>{@code @}</b> and <b>{@code :}</b> stand for
+ * themselves. Nearly all URI schemes currently in use are server-based. An
+ * authority component that does not parse in this way is considered to be
+ * registry-based.
+ *
+ * <p> The path component of a hierarchical URI is itself said to be absolute
+ * if it begins with a slash character ({@code '/'}); otherwise it is
+ * relative. The path of a hierarchical URI that is either absolute or
+ * specifies an authority is always absolute.
+ *
+ * <p> All told, then, a URI instance has the following nine components:
+ *
+ * <blockquote><table summary="Describes the components of a URI:scheme,scheme-specific-part,authority,user-info,host,port,path,query,fragment">
+ * <tr><th><i>Component</i></th><th><i>Type</i></th></tr>
+ * <tr><td>scheme</td><td>{@code String}</td></tr>
+ * <tr><td>scheme-specific-part </td><td>{@code String}</td></tr>
+ * <tr><td>authority</td><td>{@code String}</td></tr>
+ * <tr><td>user-info</td><td>{@code String}</td></tr>
+ * <tr><td>host</td><td>{@code String}</td></tr>
+ * <tr><td>port</td><td>{@code int}</td></tr>
+ * <tr><td>path</td><td>{@code String}</td></tr>
+ * <tr><td>query</td><td>{@code String}</td></tr>
+ * <tr><td>fragment</td><td>{@code String}</td></tr>
+ * </table></blockquote>
+ *
+ * In a given instance any particular component is either <i>undefined</i> or
+ * <i>defined</i> with a distinct value. Undefined string components are
+ * represented by {@code null}, while undefined integer components are
+ * represented by {@code -1}. A string component may be defined to have the
+ * empty string as its value; this is not equivalent to that component being
+ * undefined.
+ *
+ * <p> Whether a particular component is or is not defined in an instance
+ * depends upon the type of the URI being represented. An absolute URI has a
+ * scheme component. An opaque URI has a scheme, a scheme-specific part, and
+ * possibly a fragment, but has no other components. A hierarchical URI always
+ * has a path (though it may be empty) and a scheme-specific-part (which at
+ * least contains the path), and may have any of the other components. If the
+ * authority component is present and is server-based then the host component
+ * will be defined and the user-information and port components may be defined.
+ *
+ *
+ * <h4> Operations on URI instances </h4>
+ *
+ * The key operations supported by this class are those of
+ * <i>normalization</i>, <i>resolution</i>, and <i>relativization</i>.
+ *
+ * <p> <i>Normalization</i> is the process of removing unnecessary {@code "."}
+ * and {@code ".."} segments from the path component of a hierarchical URI.
+ * Each {@code "."} segment is simply removed. A {@code ".."} segment is
+ * removed only if it is preceded by a non-{@code ".."} segment.
+ * Normalization has no effect upon opaque URIs.
+ *
+ * <p> <i>Resolution</i> is the process of resolving one URI against another,
+ * <i>base</i> URI. The resulting URI is constructed from components of both
+ * URIs in the manner specified by RFC 2396, taking components from the
+ * base URI for those not specified in the original. For hierarchical URIs,
+ * the path of the original is resolved against the path of the base and then
+ * normalized. The result, for example, of resolving
+ *
+ * <blockquote>
+ * {@code docs/guide/collections/designfaq.html#28}
+ *
+ * (1)
+ * </blockquote>
+ *
+ * against the base URI {@code http://java.sun.com/j2se/1.3/} is the result
+ * URI
+ *
+ * <blockquote>
+ * {@code http://java.sun.com/j2se/1.3/docs/guide/collections/designfaq.html#28}
+ * </blockquote>
+ *
+ * Resolving the relative URI
+ *
+ * <blockquote>
+ * {@code ../../../demo/jfc/SwingSet2/src/SwingSet2.java} (2)
+ * </blockquote>
+ *
+ * against this result yields, in turn,
+ *
+ * <blockquote>
+ * {@code http://java.sun.com/j2se/1.3/demo/jfc/SwingSet2/src/SwingSet2.java}
+ * </blockquote>
+ *
+ * Resolution of both absolute and relative URIs, and of both absolute and
+ * relative paths in the case of hierarchical URIs, is supported. Resolving
+ * the URI {@code file:///~calendar} against any other URI simply yields the
+ * original URI, since it is absolute. Resolving the relative URI (2) above
+ * against the relative base URI (1) yields the normalized, but still relative,
+ * URI
+ *
+ * <blockquote>
+ * {@code demo/jfc/SwingSet2/src/SwingSet2.java}
+ * </blockquote>
+ *
+ * <p> <i>Relativization</i>, finally, is the inverse of resolution: For any
+ * two normalized URIs <i>u</i> and <i>v</i>,
+ *
+ * <blockquote>
+ * <i>u</i>{@code .relativize(}<i>u</i>{@code .resolve(}<i>v</i>{@code )).equals(}<i>v</i>{@code )} and<br>
+ * <i>u</i>{@code .resolve(}<i>u</i>{@code .relativize(}<i>v</i>{@code )).equals(}<i>v</i>{@code )} .<br>
+ * </blockquote>
+ *
+ * This operation is often useful when constructing a document containing URIs
+ * that must be made relative to the base URI of the document wherever
+ * possible. For example, relativizing the URI
+ *
+ * <blockquote>
+ * {@code http://java.sun.com/j2se/1.3/docs/guide/index.html}
+ * </blockquote>
+ *
+ * against the base URI
+ *
+ * <blockquote>
+ * {@code http://java.sun.com/j2se/1.3}
+ * </blockquote>
+ *
+ * yields the relative URI {@code docs/guide/index.html}.
+ *
+ *
+ * <h4> Character categories </h4>
+ *
+ * RFC 2396 specifies precisely which characters are permitted in the
+ * various components of a URI reference. The following categories, most of
+ * which are taken from that specification, are used below to describe these
+ * constraints:
+ *
+ * <blockquote><table cellspacing=2 summary="Describes categories alpha,digit,alphanum,unreserved,punct,reserved,escaped,and other">
+ * <tr><th valign=top><i>alpha</i></th>
+ * <td>The US-ASCII alphabetic characters,
+ * {@code 'A'} through {@code 'Z'}
+ * and {@code 'a'} through {@code 'z'}</td></tr>
+ * <tr><th valign=top><i>digit</i></th>
+ * <td>The US-ASCII decimal digit characters,
+ * {@code '0'} through {@code '9'}</td></tr>
+ * <tr><th valign=top><i>alphanum</i></th>
+ * <td>All <i>alpha</i> and <i>digit</i> characters</td></tr>
+ * <tr><th valign=top><i>unreserved</i> </th>
+ * <td>All <i>alphanum</i> characters together with those in the string
+ * {@code "_-!.~'()*"}</td></tr>
+ * <tr><th valign=top><i>punct</i></th>
+ * <td>The characters in the string {@code ",;:$&+="}</td></tr>
+ * <tr><th valign=top><i>reserved</i></th>
+ * <td>All <i>punct</i> characters together with those in the string
+ * {@code "?/[]@"}</td></tr>
+ * <tr><th valign=top><i>escaped</i></th>
+ * <td>Escaped octets, that is, triplets consisting of the percent
+ * character ({@code '%'}) followed by two hexadecimal digits
+ * ({@code '0'}-{@code '9'}, {@code 'A'}-{@code 'F'}, and
+ * {@code 'a'}-{@code 'f'})</td></tr>
+ * <tr><th valign=top><i>other</i></th>
+ * <td>The Unicode characters that are not in the US-ASCII character set,
+ * are not control characters (according to the {@link
+ * java.lang.Character#isISOControl(char) Character.isISOControl}
+ * method), and are not space characters (according to the {@link
+ * java.lang.Character#isSpaceChar(char) Character.isSpaceChar}
+ * method) <i>(<b>Deviation from RFC 2396</b>, which is
+ * limited to US-ASCII)</i></td></tr>
+ * </table></blockquote>
+ *
+ * <p><a name="legal-chars"></a> The set of all legal URI characters consists of
+ * the <i>unreserved</i>, <i>reserved</i>, <i>escaped</i>, and <i>other</i>
+ * characters.
+ *
+ *
+ * <h4> Escaped octets, quotation, encoding, and decoding </h4>
+ *
+ * RFC 2396 allows escaped octets to appear in the user-info, path, query, and
+ * fragment components. Escaping serves two purposes in URIs:
+ *
+ * <ul>
+ *
+ * <li><p> To <i>encode</i> non-US-ASCII characters when a URI is required to
+ * conform strictly to RFC 2396 by not containing any <i>other</i>
+ * characters. </p></li>
+ *
+ * <li><p> To <i>quote</i> characters that are otherwise illegal in a
+ * component. The user-info, path, query, and fragment components differ
+ * slightly in terms of which characters are considered legal and illegal.
+ * </p></li>
+ *
+ * </ul>
+ *
+ * These purposes are served in this class by three related operations:
+ *
+ * <ul>
+ *
+ * <li><p><a name="encode"></a> A character is <i>encoded</i> by replacing it
+ * with the sequence of escaped octets that represent that character in the
+ * UTF-8 character set. The Euro currency symbol ({@code '\u005Cu20AC'}),
+ * for example, is encoded as {@code "%E2%82%AC"}. <i>(<b>Deviation from
+ * RFC 2396</b>, which does not specify any particular character
+ * set.)</i> </p></li>
+ *
+ * <li><p><a name="quote"></a> An illegal character is <i>quoted</i> simply by
+ * encoding it. The space character, for example, is quoted by replacing it
+ * with {@code "%20"}. UTF-8 contains US-ASCII, hence for US-ASCII
+ * characters this transformation has exactly the effect required by
+ * RFC 2396. </p></li>
+ *
+ * <li><p><a name="decode"></a>
+ * A sequence of escaped octets is <i>decoded</i> by
+ * replacing it with the sequence of characters that it represents in the
+ * UTF-8 character set. UTF-8 contains US-ASCII, hence decoding has the
+ * effect of de-quoting any quoted US-ASCII characters as well as that of
+ * decoding any encoded non-US-ASCII characters. If a <a
+ * href="../nio/charset/CharsetDecoder.html#ce">decoding error</a> occurs
+ * when decoding the escaped octets then the erroneous octets are replaced by
+ * {@code '\u005CuFFFD'}, the Unicode replacement character. </p></li>
+ *
+ * </ul>
+ *
+ * These operations are exposed in the constructors and methods of this class
+ * as follows:
+ *
+ * <ul>
+ *
+ * <li><p> The {@linkplain #URI(java.lang.String) single-argument
+ * constructor} requires any illegal characters in its argument to be
+ * quoted and preserves any escaped octets and <i>other</i> characters that
+ * are present. </p></li>
+ *
+ * <li><p> The {@linkplain
+ * #URI(java.lang.String,java.lang.String,java.lang.String,int,java.lang.String,java.lang.String,java.lang.String)
+ * multi-argument constructors} quote illegal characters as
+ * required by the components in which they appear. The percent character
+ * ({@code '%'}) is always quoted by these constructors. Any <i>other</i>
+ * characters are preserved. </p></li>
+ *
+ * <li><p> The {@link #getRawUserInfo() getRawUserInfo}, {@link #getRawPath()
+ * getRawPath}, {@link #getRawQuery() getRawQuery}, {@link #getRawFragment()
+ * getRawFragment}, {@link #getRawAuthority() getRawAuthority}, and {@link
+ * #getRawSchemeSpecificPart() getRawSchemeSpecificPart} methods return the
+ * values of their corresponding components in raw form, without interpreting
+ * any escaped octets. The strings returned by these methods may contain
+ * both escaped octets and <i>other</i> characters, and will not contain any
+ * illegal characters. </p></li>
+ *
+ * <li><p> The {@link #getUserInfo() getUserInfo}, {@link #getPath()
+ * getPath}, {@link #getQuery() getQuery}, {@link #getFragment()
+ * getFragment}, {@link #getAuthority() getAuthority}, and {@link
+ * #getSchemeSpecificPart() getSchemeSpecificPart} methods decode any escaped
+ * octets in their corresponding components. The strings returned by these
+ * methods may contain both <i>other</i> characters and illegal characters,
+ * and will not contain any escaped octets. </p></li>
+ *
+ * <li><p> The {@link #toString() toString} method returns a URI string with
+ * all necessary quotation but which may contain <i>other</i> characters.
+ * </p></li>
+ *
+ * <li><p> The {@link #toASCIIString() toASCIIString} method returns a fully
+ * quoted and encoded URI string that does not contain any <i>other</i>
+ * characters. </p></li>
+ *
+ * </ul>
+ *
+ *
+ * <h4> Identities </h4>
+ *
+ * For any URI <i>u</i>, it is always the case that
+ *
+ * <blockquote>
+ * {@code new URI(}<i>u</i>{@code .toString()).equals(}<i>u</i>{@code )} .
+ * </blockquote>
+ *
+ * For any URI <i>u</i> that does not contain redundant syntax such as two
+ * slashes before an empty authority (as in {@code file:///tmp/} ) or a
+ * colon following a host name but no port (as in
+ * {@code http://java.sun.com:} ), and that does not encode characters
+ * except those that must be quoted, the following identities also hold:
+ * <pre>
+ * new URI(<i>u</i>.getScheme(),
+ * <i>u</i>.getSchemeSpecificPart(),
+ * <i>u</i>.getFragment())
+ * .equals(<i>u</i>)</pre>
+ * in all cases,
+ * <pre>
+ * new URI(<i>u</i>.getScheme(),
+ * <i>u</i>.getUserInfo(), <i>u</i>.getAuthority(),
+ * <i>u</i>.getPath(), <i>u</i>.getQuery(),
+ * <i>u</i>.getFragment())
+ * .equals(<i>u</i>)</pre>
+ * if <i>u</i> is hierarchical, and
+ * <pre>
+ * new URI(<i>u</i>.getScheme(),
+ * <i>u</i>.getUserInfo(), <i>u</i>.getHost(), <i>u</i>.getPort(),
+ * <i>u</i>.getPath(), <i>u</i>.getQuery(),
+ * <i>u</i>.getFragment())
+ * .equals(<i>u</i>)</pre>
+ * if <i>u</i> is hierarchical and has either no authority or a server-based
+ * authority.
+ *
+ *
+ * <h4> URIs, URLs, and URNs </h4>
+ *
+ * A URI is a uniform resource <i>identifier</i> while a URL is a uniform
+ * resource <i>locator</i>. Hence every URL is a URI, abstractly speaking, but
+ * not every URI is a URL. This is because there is another subcategory of
+ * URIs, uniform resource <i>names</i> (URNs), which name resources but do not
+ * specify how to locate them. The {@code mailto}, {@code news}, and
+ * {@code isbn} URIs shown above are examples of URNs.
+ *
+ * <p> The conceptual distinction between URIs and URLs is reflected in the
+ * differences between this class and the {@link URL} class.
+ *
+ * <p> An instance of this class represents a URI reference in the syntactic
+ * sense defined by RFC 2396. A URI may be either absolute or relative.
+ * A URI string is parsed according to the generic syntax without regard to the
+ * scheme, if any, that it specifies. No lookup of the host, if any, is
+ * performed, and no scheme-dependent stream handler is constructed. Equality,
+ * hashing, and comparison are defined strictly in terms of the character
+ * content of the instance. In other words, a URI instance is little more than
+ * a structured string that supports the syntactic, scheme-independent
+ * operations of comparison, normalization, resolution, and relativization.
+ *
+ * <p> An instance of the {@link URL} class, by contrast, represents the
+ * syntactic components of a URL together with some of the information required
+ * to access the resource that it describes. A URL must be absolute, that is,
+ * it must always specify a scheme. A URL string is parsed according to its
+ * scheme. A stream handler is always established for a URL, and in fact it is
+ * impossible to create a URL instance for a scheme for which no handler is
+ * available. Equality and hashing depend upon both the scheme and the
+ * Internet address of the host, if any; comparison is not defined. In other
+ * words, a URL is a structured string that supports the syntactic operation of
+ * resolution as well as the network I/O operations of looking up the host and
+ * opening a connection to the specified resource.
+ *
+ *
+ * @author Mark Reinhold
+ * @since 1.4
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc2279.txt">RFC 2279: UTF-8, a transformation format of ISO 10646</a>
+ * @see <a href="http://www.ietf.org/rfc/rfc2373.txt">RFC 2373: IPv6 Addressing Architecture</a>
+ * @see <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396: Uniform Resource Identifiers (URI): Generic Syntax</a>
+ * @see <a href="http://www.ietf.org/rfc/rfc2732.txt">RFC 2732: Format for Literal IPv6 Addresses in URLs</a>
+ */
+
+public final class URI
+ implements Comparable<URI>, Serializable
+{
+
+ // Note: Comments containing the word "ASSERT" indicate places where a
+ // throw of an InternalError should be replaced by an appropriate assertion
+ // statement once asserts are enabled in the build.
+
+ static final long serialVersionUID = -6052424284110960213L;
+
+
+ // -- Properties and components of this instance --
+
+ // Components of all URIs: [<scheme>:]<scheme-specific-part>[#<fragment>]
+ private transient String scheme; // null ==> relative URI
+ private transient String fragment;
+
+ // Hierarchical URI components: [//<authority>]<path>[?<query>]
+ private transient String authority; // Registry or server
+
+ // Server-based authority: [<userInfo>@]<host>[:<port>]
+ private transient String userInfo;
+ private transient String host; // null ==> registry-based
+ private transient int port = -1; // -1 ==> undefined
+
+ // Remaining components of hierarchical URIs
+ private transient String path; // null ==> opaque
+ private transient String query;
+
+ // The remaining fields may be computed on demand
+
+ private volatile transient String schemeSpecificPart;
+ private volatile transient int hash; // Zero ==> undefined
+
+ private volatile transient String decodedUserInfo = null;
+ private volatile transient String decodedAuthority = null;
+ private volatile transient String decodedPath = null;
+ private volatile transient String decodedQuery = null;
+ private volatile transient String decodedFragment = null;
+ private volatile transient String decodedSchemeSpecificPart = null;
+
+ /**
+ * The string form of this URI.
+ *
+ * @serial
+ */
+ private volatile String string; // The only serializable field
+
+
+
+ // -- Constructors and factories --
+
+ private URI() { } // Used internally
+
+ /**
+ * Constructs a URI by parsing the given string.
+ *
+ * <p> This constructor parses the given string exactly as specified by the
+ * grammar in <a
+ * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>,
+ * Appendix A, <b><i>except for the following deviations:</i></b> </p>
+ *
+ * <ul>
+ *
+ * <li><p> An empty authority component is permitted as long as it is
+ * followed by a non-empty path, a query component, or a fragment
+ * component. This allows the parsing of URIs such as
+ * {@code "file:///foo/bar"}, which seems to be the intent of
+ * RFC 2396 although the grammar does not permit it. If the
+ * authority component is empty then the user-information, host, and port
+ * components are undefined. </p></li>
+ *
+ * <li><p> Empty relative paths are permitted; this seems to be the
+ * intent of RFC 2396 although the grammar does not permit it. The
+ * primary consequence of this deviation is that a standalone fragment
+ * such as {@code "#foo"} parses as a relative URI with an empty path
+ * and the given fragment, and can be usefully <a
+ * href="#resolve-frag">resolved</a> against a base URI.
+ *
+ * <li><p> IPv4 addresses in host components are parsed rigorously, as
+ * specified by <a
+ * href="http://www.ietf.org/rfc/rfc2732.txt">RFC 2732</a>: Each
+ * element of a dotted-quad address must contain no more than three
+ * decimal digits. Each element is further constrained to have a value
+ * no greater than 255. </p></li>
+ *
+ * <li> <p> Hostnames in host components that comprise only a single
+ * domain label are permitted to start with an <i>alphanum</i>
+ * character. This seems to be the intent of <a
+ * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>
+ * section 3.2.2 although the grammar does not permit it. The
+ * consequence of this deviation is that the authority component of a
+ * hierarchical URI such as {@code s://123}, will parse as a server-based
+ * authority. </p></li>
+ *
+ * <li><p> IPv6 addresses are permitted for the host component. An IPv6
+ * address must be enclosed in square brackets ({@code '['} and
+ * {@code ']'}) as specified by <a
+ * href="http://www.ietf.org/rfc/rfc2732.txt">RFC 2732</a>. The
+ * IPv6 address itself must parse according to <a
+ * href="http://www.ietf.org/rfc/rfc2373.txt">RFC 2373</a>. IPv6
+ * addresses are further constrained to describe no more than sixteen
+ * bytes of address information, a constraint implicit in RFC 2373
+ * but not expressible in the grammar. </p></li>
+ *
+ * <li><p> Characters in the <i>other</i> category are permitted wherever
+ * RFC 2396 permits <i>escaped</i> octets, that is, in the
+ * user-information, path, query, and fragment components, as well as in
+ * the authority component if the authority is registry-based. This
+ * allows URIs to contain Unicode characters beyond those in the US-ASCII
+ * character set. </p></li>
+ *
+ * </ul>
+ *
+ * @param str The string to be parsed into a URI
+ *
+ * @throws NullPointerException
+ * If {@code str} is {@code null}
+ *
+ * @throws URISyntaxException
+ * If the given string violates RFC 2396, as augmented
+ * by the above deviations
+ */
+ public URI(String str) throws URISyntaxException {
+ new Parser(str).parse(false);
+ }
+
+ /**
+ * Constructs a hierarchical URI from the given components.
+ *
+ * <p> If a scheme is given then the path, if also given, must either be
+ * empty or begin with a slash character ({@code '/'}). Otherwise a
+ * component of the new URI may be left undefined by passing {@code null}
+ * for the corresponding parameter or, in the case of the {@code port}
+ * parameter, by passing {@code -1}.
+ *
+ * <p> This constructor first builds a URI string from the given components
+ * according to the rules specified in <a
+ * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>,
+ * section 5.2, step 7: </p>
+ *
+ * <ol>
+ *
+ * <li><p> Initially, the result string is empty. </p></li>
+ *
+ * <li><p> If a scheme is given then it is appended to the result,
+ * followed by a colon character ({@code ':'}). </p></li>
+ *
+ * <li><p> If user information, a host, or a port are given then the
+ * string {@code "//"} is appended. </p></li>
+ *
+ * <li><p> If user information is given then it is appended, followed by
+ * a commercial-at character ({@code '@'}). Any character not in the
+ * <i>unreserved</i>, <i>punct</i>, <i>escaped</i>, or <i>other</i>
+ * categories is <a href="#quote">quoted</a>. </p></li>
+ *
+ * <li><p> If a host is given then it is appended. If the host is a
+ * literal IPv6 address but is not enclosed in square brackets
+ * ({@code '['} and {@code ']'}) then the square brackets are added.
+ * </p></li>
+ *
+ * <li><p> If a port number is given then a colon character
+ * ({@code ':'}) is appended, followed by the port number in decimal.
+ * </p></li>
+ *
+ * <li><p> If a path is given then it is appended. Any character not in
+ * the <i>unreserved</i>, <i>punct</i>, <i>escaped</i>, or <i>other</i>
+ * categories, and not equal to the slash character ({@code '/'}) or the
+ * commercial-at character ({@code '@'}), is quoted. </p></li>
+ *
+ * <li><p> If a query is given then a question-mark character
+ * ({@code '?'}) is appended, followed by the query. Any character that
+ * is not a <a href="#legal-chars">legal URI character</a> is quoted.
+ * </p></li>
+ *
+ * <li><p> Finally, if a fragment is given then a hash character
+ * ({@code '#'}) is appended, followed by the fragment. Any character
+ * that is not a legal URI character is quoted. </p></li>
+ *
+ * </ol>
+ *
+ * <p> The resulting URI string is then parsed as if by invoking the {@link
+ * #URI(String)} constructor and then invoking the {@link
+ * #parseServerAuthority()} method upon the result; this may cause a {@link
+ * URISyntaxException} to be thrown. </p>
+ *
+ * @param scheme Scheme name
+ * @param userInfo User name and authorization information
+ * @param host Host name
+ * @param port Port number
+ * @param path Path
+ * @param query Query
+ * @param fragment Fragment
+ *
+ * @throws URISyntaxException
+ * If both a scheme and a path are given but the path is relative,
+ * if the URI string constructed from the given components violates
+ * RFC 2396, or if the authority component of the string is
+ * present but cannot be parsed as a server-based authority
+ */
+ public URI(String scheme,
+ String userInfo, String host, int port,
+ String path, String query, String fragment)
+ throws URISyntaxException
+ {
+ String s = toString(scheme, null,
+ null, userInfo, host, port,
+ path, query, fragment);
+ checkPath(s, scheme, path);
+ new Parser(s).parse(true);
+ }
+
+ /**
+ * Constructs a hierarchical URI from the given components.
+ *
+ * <p> If a scheme is given then the path, if also given, must either be
+ * empty or begin with a slash character ({@code '/'}). Otherwise a
+ * component of the new URI may be left undefined by passing {@code null}
+ * for the corresponding parameter.
+ *
+ * <p> This constructor first builds a URI string from the given components
+ * according to the rules specified in <a
+ * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>,
+ * section 5.2, step 7: </p>
+ *
+ * <ol>
+ *
+ * <li><p> Initially, the result string is empty. </p></li>
+ *
+ * <li><p> If a scheme is given then it is appended to the result,
+ * followed by a colon character ({@code ':'}). </p></li>
+ *
+ * <li><p> If an authority is given then the string {@code "//"} is
+ * appended, followed by the authority. If the authority contains a
+ * literal IPv6 address then the address must be enclosed in square
+ * brackets ({@code '['} and {@code ']'}). Any character not in the
+ * <i>unreserved</i>, <i>punct</i>, <i>escaped</i>, or <i>other</i>
+ * categories, and not equal to the commercial-at character
+ * ({@code '@'}), is <a href="#quote">quoted</a>. </p></li>
+ *
+ * <li><p> If a path is given then it is appended. Any character not in
+ * the <i>unreserved</i>, <i>punct</i>, <i>escaped</i>, or <i>other</i>
+ * categories, and not equal to the slash character ({@code '/'}) or the
+ * commercial-at character ({@code '@'}), is quoted. </p></li>
+ *
+ * <li><p> If a query is given then a question-mark character
+ * ({@code '?'}) is appended, followed by the query. Any character that
+ * is not a <a href="#legal-chars">legal URI character</a> is quoted.
+ * </p></li>
+ *
+ * <li><p> Finally, if a fragment is given then a hash character
+ * ({@code '#'}) is appended, followed by the fragment. Any character
+ * that is not a legal URI character is quoted. </p></li>
+ *
+ * </ol>
+ *
+ * <p> The resulting URI string is then parsed as if by invoking the {@link
+ * #URI(String)} constructor and then invoking the {@link
+ * #parseServerAuthority()} method upon the result; this may cause a {@link
+ * URISyntaxException} to be thrown. </p>
+ *
+ * @param scheme Scheme name
+ * @param authority Authority
+ * @param path Path
+ * @param query Query
+ * @param fragment Fragment
+ *
+ * @throws URISyntaxException
+ * If both a scheme and a path are given but the path is relative,
+ * if the URI string constructed from the given components violates
+ * RFC 2396, or if the authority component of the string is
+ * present but cannot be parsed as a server-based authority
+ */
+ public URI(String scheme,
+ String authority,
+ String path, String query, String fragment)
+ throws URISyntaxException
+ {
+ String s = toString(scheme, null,
+ authority, null, null, -1,
+ path, query, fragment);
+ checkPath(s, scheme, path);
+ new Parser(s).parse(false);
+ }
+
+ /**
+ * Constructs a hierarchical URI from the given components.
+ *
+ * <p> A component may be left undefined by passing {@code null}.
+ *
+ * <p> This convenience constructor works as if by invoking the
+ * seven-argument constructor as follows:
+ *
+ * <blockquote>
+ * {@code new} {@link #URI(String, String, String, int, String, String, String)
+ * URI}{@code (scheme, null, host, -1, path, null, fragment);}
+ * </blockquote>
+ *
+ * @param scheme Scheme name
+ * @param host Host name
+ * @param path Path
+ * @param fragment Fragment
+ *
+ * @throws URISyntaxException
+ * If the URI string constructed from the given components
+ * violates RFC 2396
+ */
+ public URI(String scheme, String host, String path, String fragment)
+ throws URISyntaxException
+ {
+ this(scheme, null, host, -1, path, null, fragment);
+ }
+
+ /**
+ * Constructs a URI from the given components.
+ *
+ * <p> A component may be left undefined by passing {@code null}.
+ *
+ * <p> This constructor first builds a URI in string form using the given
+ * components as follows: </p>
+ *
+ * <ol>
+ *
+ * <li><p> Initially, the result string is empty. </p></li>
+ *
+ * <li><p> If a scheme is given then it is appended to the result,
+ * followed by a colon character ({@code ':'}). </p></li>
+ *
+ * <li><p> If a scheme-specific part is given then it is appended. Any
+ * character that is not a <a href="#legal-chars">legal URI character</a>
+ * is <a href="#quote">quoted</a>. </p></li>
+ *
+ * <li><p> Finally, if a fragment is given then a hash character
+ * ({@code '#'}) is appended to the string, followed by the fragment.
+ * Any character that is not a legal URI character is quoted. </p></li>
+ *
+ * </ol>
+ *
+ * <p> The resulting URI string is then parsed in order to create the new
+ * URI instance as if by invoking the {@link #URI(String)} constructor;
+ * this may cause a {@link URISyntaxException} to be thrown. </p>
+ *
+ * @param scheme Scheme name
+ * @param ssp Scheme-specific part
+ * @param fragment Fragment
+ *
+ * @throws URISyntaxException
+ * If the URI string constructed from the given components
+ * violates RFC 2396
+ */
+ public URI(String scheme, String ssp, String fragment)
+ throws URISyntaxException
+ {
+ new Parser(toString(scheme, ssp,
+ null, null, null, -1,
+ null, null, fragment))
+ .parse(false);
+ }
+
+ /**
+ * Creates a URI by parsing the given string.
+ *
+ * <p> This convenience factory method works as if by invoking the {@link
+ * #URI(String)} constructor; any {@link URISyntaxException} thrown by the
+ * constructor is caught and wrapped in a new {@link
+ * IllegalArgumentException} object, which is then thrown.
+ *
+ * <p> This method is provided for use in situations where it is known that
+ * the given string is a legal URI, for example for URI constants declared
+ * within in a program, and so it would be considered a programming error
+ * for the string not to parse as such. The constructors, which throw
+ * {@link URISyntaxException} directly, should be used situations where a
+ * URI is being constructed from user input or from some other source that
+ * may be prone to errors. </p>
+ *
+ * @param str The string to be parsed into a URI
+ * @return The new URI
+ *
+ * @throws NullPointerException
+ * If {@code str} is {@code null}
+ *
+ * @throws IllegalArgumentException
+ * If the given string violates RFC 2396
+ */
+ public static URI create(String str) {
+ try {
+ return new URI(str);
+ } catch (URISyntaxException x) {
+ throw new IllegalArgumentException(x.getMessage(), x);
+ }
+ }
+
+
+ // -- Operations --
+
+ /**
+ * Attempts to parse this URI's authority component, if defined, into
+ * user-information, host, and port components.
+ *
+ * <p> If this URI's authority component has already been recognized as
+ * being server-based then it will already have been parsed into
+ * user-information, host, and port components. In this case, or if this
+ * URI has no authority component, this method simply returns this URI.
+ *
+ * <p> Otherwise this method attempts once more to parse the authority
+ * component into user-information, host, and port components, and throws
+ * an exception describing why the authority component could not be parsed
+ * in that way.
+ *
+ * <p> This method is provided because the generic URI syntax specified in
+ * <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>
+ * cannot always distinguish a malformed server-based authority from a
+ * legitimate registry-based authority. It must therefore treat some
+ * instances of the former as instances of the latter. The authority
+ * component in the URI string {@code "//foo:bar"}, for example, is not a
+ * legal server-based authority but it is legal as a registry-based
+ * authority.
+ *
+ * <p> In many common situations, for example when working URIs that are
+ * known to be either URNs or URLs, the hierarchical URIs being used will
+ * always be server-based. They therefore must either be parsed as such or
+ * treated as an error. In these cases a statement such as
+ *
+ * <blockquote>
+ * {@code URI }<i>u</i>{@code = new URI(str).parseServerAuthority();}
+ * </blockquote>
+ *
+ * <p> can be used to ensure that <i>u</i> always refers to a URI that, if
+ * it has an authority component, has a server-based authority with proper
+ * user-information, host, and port components. Invoking this method also
+ * ensures that if the authority could not be parsed in that way then an
+ * appropriate diagnostic message can be issued based upon the exception
+ * that is thrown. </p>
+ *
+ * @return A URI whose authority field has been parsed
+ * as a server-based authority
+ *
+ * @throws URISyntaxException
+ * If the authority component of this URI is defined
+ * but cannot be parsed as a server-based authority
+ * according to RFC 2396
+ */
+ public URI parseServerAuthority()
+ throws URISyntaxException
+ {
+ // We could be clever and cache the error message and index from the
+ // exception thrown during the original parse, but that would require
+ // either more fields or a more-obscure representation.
+ if ((host != null) || (authority == null))
+ return this;
+ defineString();
+ new Parser(string).parse(true);
+ return this;
+ }
+
+ /**
+ * Normalizes this URI's path.
+ *
+ * <p> If this URI is opaque, or if its path is already in normal form,
+ * then this URI is returned. Otherwise a new URI is constructed that is
+ * identical to this URI except that its path is computed by normalizing
+ * this URI's path in a manner consistent with <a
+ * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>,
+ * section 5.2, step 6, sub-steps c through f; that is:
+ * </p>
+ *
+ * <ol>
+ *
+ * <li><p> All {@code "."} segments are removed. </p></li>
+ *
+ * <li><p> If a {@code ".."} segment is preceded by a non-{@code ".."}
+ * segment then both of these segments are removed. This step is
+ * repeated until it is no longer applicable. </p></li>
+ *
+ * <li><p> If the path is relative, and if its first segment contains a
+ * colon character ({@code ':'}), then a {@code "."} segment is
+ * prepended. This prevents a relative URI with a path such as
+ * {@code "a:b/c/d"} from later being re-parsed as an opaque URI with a
+ * scheme of {@code "a"} and a scheme-specific part of {@code "b/c/d"}.
+ * <b><i>(Deviation from RFC 2396)</i></b> </p></li>
+ *
+ * </ol>
+ *
+ * <p> A normalized path will begin with one or more {@code ".."} segments
+ * if there were insufficient non-{@code ".."} segments preceding them to
+ * allow their removal. A normalized path will begin with a {@code "."}
+ * segment if one was inserted by step 3 above. Otherwise, a normalized
+ * path will not contain any {@code "."} or {@code ".."} segments. </p>
+ *
+ * @return A URI equivalent to this URI,
+ * but whose path is in normal form
+ */
+ public URI normalize() {
+ return normalize(this);
+ }
+
+ /**
+ * Resolves the given URI against this URI.
+ *
+ * <p> If the given URI is already absolute, or if this URI is opaque, then
+ * the given URI is returned.
+ *
+ * <p><a name="resolve-frag"></a> If the given URI's fragment component is
+ * defined, its path component is empty, and its scheme, authority, and
+ * query components are undefined, then a URI with the given fragment but
+ * with all other components equal to those of this URI is returned. This
+ * allows a URI representing a standalone fragment reference, such as
+ * {@code "#foo"}, to be usefully resolved against a base URI.
+ *
+ * <p> Otherwise this method constructs a new hierarchical URI in a manner
+ * consistent with <a
+ * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>,
+ * section 5.2; that is: </p>
+ *
+ * <ol>
+ *
+ * <li><p> A new URI is constructed with this URI's scheme and the given
+ * URI's query and fragment components. </p></li>
+ *
+ * <li><p> If the given URI has an authority component then the new URI's
+ * authority and path are taken from the given URI. </p></li>
+ *
+ * <li><p> Otherwise the new URI's authority component is copied from
+ * this URI, and its path is computed as follows: </p>
+ *
+ * <ol>
+ *
+ * <li><p> If the given URI's path is absolute then the new URI's path
+ * is taken from the given URI. </p></li>
+ *
+ * <li><p> Otherwise the given URI's path is relative, and so the new
+ * URI's path is computed by resolving the path of the given URI
+ * against the path of this URI. This is done by concatenating all but
+ * the last segment of this URI's path, if any, with the given URI's
+ * path and then normalizing the result as if by invoking the {@link
+ * #normalize() normalize} method. </p></li>
+ *
+ * </ol></li>
+ *
+ * </ol>
+ *
+ * <p> The result of this method is absolute if, and only if, either this
+ * URI is absolute or the given URI is absolute. </p>
+ *
+ * @param uri The URI to be resolved against this URI
+ * @return The resulting URI
+ *
+ * @throws NullPointerException
+ * If {@code uri} is {@code null}
+ */
+ public URI resolve(URI uri) {
+ return resolve(this, uri);
+ }
+
+ /**
+ * Constructs a new URI by parsing the given string and then resolving it
+ * against this URI.
+ *
+ * <p> This convenience method works as if invoking it were equivalent to
+ * evaluating the expression {@link #resolve(java.net.URI)
+ * resolve}{@code (URI.}{@link #create(String) create}{@code (str))}. </p>
+ *
+ * @param str The string to be parsed into a URI
+ * @return The resulting URI
+ *
+ * @throws NullPointerException
+ * If {@code str} is {@code null}
+ *
+ * @throws IllegalArgumentException
+ * If the given string violates RFC 2396
+ */
+ public URI resolve(String str) {
+ return resolve(URI.create(str));
+ }
+
+ /**
+ * Relativizes the given URI against this URI.
+ *
+ * <p> The relativization of the given URI against this URI is computed as
+ * follows: </p>
+ *
+ * <ol>
+ *
+ * <li><p> If either this URI or the given URI are opaque, or if the
+ * scheme and authority components of the two URIs are not identical, or
+ * if the path of this URI is not a prefix of the path of the given URI,
+ * then the given URI is returned. </p></li>
+ *
+ * <li><p> Otherwise a new relative hierarchical URI is constructed with
+ * query and fragment components taken from the given URI and with a path
+ * component computed by removing this URI's path from the beginning of
+ * the given URI's path. </p></li>
+ *
+ * </ol>
+ *
+ * @param uri The URI to be relativized against this URI
+ * @return The resulting URI
+ *
+ * @throws NullPointerException
+ * If {@code uri} is {@code null}
+ */
+ public URI relativize(URI uri) {
+ return relativize(this, uri);
+ }
+
+ /**
+ * Constructs a URL from this URI.
+ *
+ * <p> This convenience method works as if invoking it were equivalent to
+ * evaluating the expression {@code new URL(this.toString())} after
+ * first checking that this URI is absolute. </p>
+ *
+ * @return A URL constructed from this URI
+ *
+ * @throws IllegalArgumentException
+ * If this URL is not absolute
+ *
+ * @throws MalformedURLException
+ * If a protocol handler for the URL could not be found,
+ * or if some other error occurred while constructing the URL
+ */
+ public URL toURL()
+ throws MalformedURLException {
+ if (!isAbsolute())
+ throw new IllegalArgumentException("URI is not absolute");
+ return new URL(toString());
+ }
+
+ // -- Component access methods --
+
+ /**
+ * Returns the scheme component of this URI.
+ *
+ * <p> The scheme component of a URI, if defined, only contains characters
+ * in the <i>alphanum</i> category and in the string {@code "-.+"}. A
+ * scheme always starts with an <i>alpha</i> character. <p>
+ *
+ * The scheme component of a URI cannot contain escaped octets, hence this
+ * method does not perform any decoding.
+ *
+ * @return The scheme component of this URI,
+ * or {@code null} if the scheme is undefined
+ */
+ public String getScheme() {
+ return scheme;
+ }
+
+ /**
+ * Tells whether or not this URI is absolute.
+ *
+ * <p> A URI is absolute if, and only if, it has a scheme component. </p>
+ *
+ * @return {@code true} if, and only if, this URI is absolute
+ */
+ public boolean isAbsolute() {
+ return scheme != null;
+ }
+
+ /**
+ * Tells whether or not this URI is opaque.
+ *
+ * <p> A URI is opaque if, and only if, it is absolute and its
+ * scheme-specific part does not begin with a slash character ('/').
+ * An opaque URI has a scheme, a scheme-specific part, and possibly
+ * a fragment; all other components are undefined. </p>
+ *
+ * @return {@code true} if, and only if, this URI is opaque
+ */
+ public boolean isOpaque() {
+ return path == null;
+ }
+
+ /**
+ * Returns the raw scheme-specific part of this URI. The scheme-specific
+ * part is never undefined, though it may be empty.
+ *
+ * <p> The scheme-specific part of a URI only contains legal URI
+ * characters. </p>
+ *
+ * @return The raw scheme-specific part of this URI
+ * (never {@code null})
+ */
+ public String getRawSchemeSpecificPart() {
+ defineSchemeSpecificPart();
+ return schemeSpecificPart;
+ }
+
+ /**
+ * Returns the decoded scheme-specific part of this URI.
+ *
+ * <p> The string returned by this method is equal to that returned by the
+ * {@link #getRawSchemeSpecificPart() getRawSchemeSpecificPart} method
+ * except that all sequences of escaped octets are <a
+ * href="#decode">decoded</a>. </p>
+ *
+ * @return The decoded scheme-specific part of this URI
+ * (never {@code null})
+ */
+ public String getSchemeSpecificPart() {
+ if (decodedSchemeSpecificPart == null)
+ decodedSchemeSpecificPart = decode(getRawSchemeSpecificPart());
+ return decodedSchemeSpecificPart;
+ }
+
+ /**
+ * Returns the raw authority component of this URI.
+ *
+ * <p> The authority component of a URI, if defined, only contains the
+ * commercial-at character ({@code '@'}) and characters in the
+ * <i>unreserved</i>, <i>punct</i>, <i>escaped</i>, and <i>other</i>
+ * categories. If the authority is server-based then it is further
+ * constrained to have valid user-information, host, and port
+ * components. </p>
+ *
+ * @return The raw authority component of this URI,
+ * or {@code null} if the authority is undefined
+ */
+ public String getRawAuthority() {
+ return authority;
+ }
+
+ /**
+ * Returns the decoded authority component of this URI.
+ *
+ * <p> The string returned by this method is equal to that returned by the
+ * {@link #getRawAuthority() getRawAuthority} method except that all
+ * sequences of escaped octets are <a href="#decode">decoded</a>. </p>
+ *
+ * @return The decoded authority component of this URI,
+ * or {@code null} if the authority is undefined
+ */
+ public String getAuthority() {
+ if (decodedAuthority == null)
+ decodedAuthority = decode(authority);
+ return decodedAuthority;
+ }
+
+ /**
+ * Returns the raw user-information component of this URI.
+ *
+ * <p> The user-information component of a URI, if defined, only contains
+ * characters in the <i>unreserved</i>, <i>punct</i>, <i>escaped</i>, and
+ * <i>other</i> categories. </p>
+ *
+ * @return The raw user-information component of this URI,
+ * or {@code null} if the user information is undefined
+ */
+ public String getRawUserInfo() {
+ return userInfo;
+ }
+
+ /**
+ * Returns the decoded user-information component of this URI.
+ *
+ * <p> The string returned by this method is equal to that returned by the
+ * {@link #getRawUserInfo() getRawUserInfo} method except that all
+ * sequences of escaped octets are <a href="#decode">decoded</a>. </p>
+ *
+ * @return The decoded user-information component of this URI,
+ * or {@code null} if the user information is undefined
+ */
+ public String getUserInfo() {
+ if ((decodedUserInfo == null) && (userInfo != null))
+ decodedUserInfo = decode(userInfo);
+ return decodedUserInfo;
+ }
+
+ /**
+ * Returns the host component of this URI.
+ *
+ * <p> The host component of a URI, if defined, will have one of the
+ * following forms: </p>
+ *
+ * <ul>
+ *
+ * <li><p> A domain name consisting of one or more <i>labels</i>
+ * separated by period characters ({@code '.'}), optionally followed by
+ * a period character. Each label consists of <i>alphanum</i> characters
+ * as well as hyphen characters ({@code '-'}), though hyphens never
+ * occur as the first or last characters in a label. The rightmost
+ * label of a domain name consisting of two or more labels, begins
+ * with an <i>alpha</i> character. </li>
+ *
+ * <li><p> A dotted-quad IPv4 address of the form
+ * <i>digit</i>{@code +.}<i>digit</i>{@code +.}<i>digit</i>{@code +.}<i>digit</i>{@code +},
+ * where no <i>digit</i> sequence is longer than three characters and no
+ * sequence has a value larger than 255. </p></li>
+ *
+ * <li><p> An IPv6 address enclosed in square brackets ({@code '['} and
+ * {@code ']'}) and consisting of hexadecimal digits, colon characters
+ * ({@code ':'}), and possibly an embedded IPv4 address. The full
+ * syntax of IPv6 addresses is specified in <a
+ * href="http://www.ietf.org/rfc/rfc2373.txt"><i>RFC 2373: IPv6
+ * Addressing Architecture</i></a>. </p></li>
+ *
+ * </ul>
+ *
+ * The host component of a URI cannot contain escaped octets, hence this
+ * method does not perform any decoding.
+ *
+ * @return The host component of this URI,
+ * or {@code null} if the host is undefined
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * Returns the port number of this URI.
+ *
+ * <p> The port component of a URI, if defined, is a non-negative
+ * integer. </p>
+ *
+ * @return The port component of this URI,
+ * or {@code -1} if the port is undefined
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * Returns the raw path component of this URI.
+ *
+ * <p> The path component of a URI, if defined, only contains the slash
+ * character ({@code '/'}), the commercial-at character ({@code '@'}),
+ * and characters in the <i>unreserved</i>, <i>punct</i>, <i>escaped</i>,
+ * and <i>other</i> categories. </p>
+ *
+ * @return The path component of this URI,
+ * or {@code null} if the path is undefined
+ */
+ public String getRawPath() {
+ return path;
+ }
+
+ /**
+ * Returns the decoded path component of this URI.
+ *
+ * <p> The string returned by this method is equal to that returned by the
+ * {@link #getRawPath() getRawPath} method except that all sequences of
+ * escaped octets are <a href="#decode">decoded</a>. </p>
+ *
+ * @return The decoded path component of this URI,
+ * or {@code null} if the path is undefined
+ */
+ public String getPath() {
+ if ((decodedPath == null) && (path != null))
+ decodedPath = decode(path);
+ return decodedPath;
+ }
+
+ /**
+ * Returns the raw query component of this URI.
+ *
+ * <p> The query component of a URI, if defined, only contains legal URI
+ * characters. </p>
+ *
+ * @return The raw query component of this URI,
+ * or {@code null} if the query is undefined
+ */
+ public String getRawQuery() {
+ return query;
+ }
+
+ /**
+ * Returns the decoded query component of this URI.
+ *
+ * <p> The string returned by this method is equal to that returned by the
+ * {@link #getRawQuery() getRawQuery} method except that all sequences of
+ * escaped octets are <a href="#decode">decoded</a>. </p>
+ *
+ * @return The decoded query component of this URI,
+ * or {@code null} if the query is undefined
+ */
+ public String getQuery() {
+ if ((decodedQuery == null) && (query != null))
+ decodedQuery = decode(query);
+ return decodedQuery;
+ }
+
+ /**
+ * Returns the raw fragment component of this URI.
+ *
+ * <p> The fragment component of a URI, if defined, only contains legal URI
+ * characters. </p>
+ *
+ * @return The raw fragment component of this URI,
+ * or {@code null} if the fragment is undefined
+ */
+ public String getRawFragment() {
+ return fragment;
+ }
+
+ /**
+ * Returns the decoded fragment component of this URI.
+ *
+ * <p> The string returned by this method is equal to that returned by the
+ * {@link #getRawFragment() getRawFragment} method except that all
+ * sequences of escaped octets are <a href="#decode">decoded</a>. </p>
+ *
+ * @return The decoded fragment component of this URI,
+ * or {@code null} if the fragment is undefined
+ */
+ public String getFragment() {
+ if ((decodedFragment == null) && (fragment != null))
+ decodedFragment = decode(fragment);
+ return decodedFragment;
+ }
+
+
+ // -- Equality, comparison, hash code, toString, and serialization --
+
+ /**
+ * Tests this URI for equality with another object.
+ *
+ * <p> If the given object is not a URI then this method immediately
+ * returns {@code false}.
+ *
+ * <p> For two URIs to be considered equal requires that either both are
+ * opaque or both are hierarchical. Their schemes must either both be
+ * undefined or else be equal without regard to case. Their fragments
+ * must either both be undefined or else be equal.
+ *
+ * <p> For two opaque URIs to be considered equal, their scheme-specific
+ * parts must be equal.
+ *
+ * <p> For two hierarchical URIs to be considered equal, their paths must
+ * be equal and their queries must either both be undefined or else be
+ * equal. Their authorities must either both be undefined, or both be
+ * registry-based, or both be server-based. If their authorities are
+ * defined and are registry-based, then they must be equal. If their
+ * authorities are defined and are server-based, then their hosts must be
+ * equal without regard to case, their port numbers must be equal, and
+ * their user-information components must be equal.
+ *
+ * <p> When testing the user-information, path, query, fragment, authority,
+ * or scheme-specific parts of two URIs for equality, the raw forms rather
+ * than the encoded forms of these components are compared and the
+ * hexadecimal digits of escaped octets are compared without regard to
+ * case.
+ *
+ * <p> This method satisfies the general contract of the {@link
+ * java.lang.Object#equals(Object) Object.equals} method. </p>
+ *
+ * @param ob The object to which this object is to be compared
+ *
+ * @return {@code true} if, and only if, the given object is a URI that
+ * is identical to this URI
+ */
+ public boolean equals(Object ob) {
+ if (ob == this)
+ return true;
+ if (!(ob instanceof URI))
+ return false;
+ URI that = (URI)ob;
+ if (this.isOpaque() != that.isOpaque()) return false;
+ if (!equalIgnoringCase(this.scheme, that.scheme)) return false;
+ if (!equal(this.fragment, that.fragment)) return false;
+
+ // Opaque
+ if (this.isOpaque())
+ return equal(this.schemeSpecificPart, that.schemeSpecificPart);
+
+ // Hierarchical
+ if (!equal(this.path, that.path)) return false;
+ if (!equal(this.query, that.query)) return false;
+
+ // Authorities
+ if (this.authority == that.authority) return true;
+ if (this.host != null) {
+ // Server-based
+ if (!equal(this.userInfo, that.userInfo)) return false;
+ if (!equalIgnoringCase(this.host, that.host)) return false;
+ if (this.port != that.port) return false;
+ } else if (this.authority != null) {
+ // Registry-based
+ if (!equal(this.authority, that.authority)) return false;
+ } else if (this.authority != that.authority) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a hash-code value for this URI. The hash code is based upon all
+ * of the URI's components, and satisfies the general contract of the
+ * {@link java.lang.Object#hashCode() Object.hashCode} method.
+ *
+ * @return A hash-code value for this URI
+ */
+ public int hashCode() {
+ if (hash != 0)
+ return hash;
+ int h = hashIgnoringCase(0, scheme);
+ h = hash(h, fragment);
+ if (isOpaque()) {
+ h = hash(h, schemeSpecificPart);
+ } else {
+ h = hash(h, path);
+ h = hash(h, query);
+ if (host != null) {
+ h = hash(h, userInfo);
+ h = hashIgnoringCase(h, host);
+ h += 1949 * port;
+ } else {
+ h = hash(h, authority);
+ }
+ }
+ hash = h;
+ return h;
+ }
+
+ /**
+ * Compares this URI to another object, which must be a URI.
+ *
+ * <p> When comparing corresponding components of two URIs, if one
+ * component is undefined but the other is defined then the first is
+ * considered to be less than the second. Unless otherwise noted, string
+ * components are ordered according to their natural, case-sensitive
+ * ordering as defined by the {@link java.lang.String#compareTo(Object)
+ * String.compareTo} method. String components that are subject to
+ * encoding are compared by comparing their raw forms rather than their
+ * encoded forms.
+ *
+ * <p> The ordering of URIs is defined as follows: </p>
+ *
+ * <ul>
+ *
+ * <li><p> Two URIs with different schemes are ordered according the
+ * ordering of their schemes, without regard to case. </p></li>
+ *
+ * <li><p> A hierarchical URI is considered to be less than an opaque URI
+ * with an identical scheme. </p></li>
+ *
+ * <li><p> Two opaque URIs with identical schemes are ordered according
+ * to the ordering of their scheme-specific parts. </p></li>
+ *
+ * <li><p> Two opaque URIs with identical schemes and scheme-specific
+ * parts are ordered according to the ordering of their
+ * fragments. </p></li>
+ *
+ * <li><p> Two hierarchical URIs with identical schemes are ordered
+ * according to the ordering of their authority components: </p>
+ *
+ * <ul>
+ *
+ * <li><p> If both authority components are server-based then the URIs
+ * are ordered according to their user-information components; if these
+ * components are identical then the URIs are ordered according to the
+ * ordering of their hosts, without regard to case; if the hosts are
+ * identical then the URIs are ordered according to the ordering of
+ * their ports. </p></li>
+ *
+ * <li><p> If one or both authority components are registry-based then
+ * the URIs are ordered according to the ordering of their authority
+ * components. </p></li>
+ *
+ * </ul></li>
+ *
+ * <li><p> Finally, two hierarchical URIs with identical schemes and
+ * authority components are ordered according to the ordering of their
+ * paths; if their paths are identical then they are ordered according to
+ * the ordering of their queries; if the queries are identical then they
+ * are ordered according to the order of their fragments. </p></li>
+ *
+ * </ul>
+ *
+ * <p> This method satisfies the general contract of the {@link
+ * java.lang.Comparable#compareTo(Object) Comparable.compareTo}
+ * method. </p>
+ *
+ * @param that
+ * The object to which this URI is to be compared
+ *
+ * @return A negative integer, zero, or a positive integer as this URI is
+ * less than, equal to, or greater than the given URI
+ *
+ * @throws ClassCastException
+ * If the given object is not a URI
+ */
+ public int compareTo(URI that) {
+ int c;
+
+ if ((c = compareIgnoringCase(this.scheme, that.scheme)) != 0)
+ return c;
+
+ if (this.isOpaque()) {
+ if (that.isOpaque()) {
+ // Both opaque
+ if ((c = compare(this.schemeSpecificPart,
+ that.schemeSpecificPart)) != 0)
+ return c;
+ return compare(this.fragment, that.fragment);
+ }
+ return +1; // Opaque > hierarchical
+ } else if (that.isOpaque()) {
+ return -1; // Hierarchical < opaque
+ }
+
+ // Hierarchical
+ if ((this.host != null) && (that.host != null)) {
+ // Both server-based
+ if ((c = compare(this.userInfo, that.userInfo)) != 0)
+ return c;
+ if ((c = compareIgnoringCase(this.host, that.host)) != 0)
+ return c;
+ if ((c = this.port - that.port) != 0)
+ return c;
+ } else {
+ // If one or both authorities are registry-based then we simply
+ // compare them in the usual, case-sensitive way. If one is
+ // registry-based and one is server-based then the strings are
+ // guaranteed to be unequal, hence the comparison will never return
+ // zero and the compareTo and equals methods will remain
+ // consistent.
+ if ((c = compare(this.authority, that.authority)) != 0) return c;
+ }
+
+ if ((c = compare(this.path, that.path)) != 0) return c;
+ if ((c = compare(this.query, that.query)) != 0) return c;
+ return compare(this.fragment, that.fragment);
+ }
+
+ /**
+ * Returns the content of this URI as a string.
+ *
+ * <p> If this URI was created by invoking one of the constructors in this
+ * class then a string equivalent to the original input string, or to the
+ * string computed from the originally-given components, as appropriate, is
+ * returned. Otherwise this URI was created by normalization, resolution,
+ * or relativization, and so a string is constructed from this URI's
+ * components according to the rules specified in <a
+ * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>,
+ * section 5.2, step 7. </p>
+ *
+ * @return The string form of this URI
+ */
+ public String toString() {
+ defineString();
+ return string;
+ }
+
+ /**
+ * Returns the content of this URI as a US-ASCII string.
+ *
+ * <p> If this URI does not contain any characters in the <i>other</i>
+ * category then an invocation of this method will return the same value as
+ * an invocation of the {@link #toString() toString} method. Otherwise
+ * this method works as if by invoking that method and then <a
+ * href="#encode">encoding</a> the result. </p>
+ *
+ * @return The string form of this URI, encoded as needed
+ * so that it only contains characters in the US-ASCII
+ * charset
+ */
+ public String toASCIIString() {
+ defineString();
+ return encode(string);
+ }
+
+
+ // -- Serialization support --
+
+ /**
+ * Saves the content of this URI to the given serial stream.
+ *
+ * <p> The only serializable field of a URI instance is its {@code string}
+ * field. That field is given a value, if it does not have one already,
+ * and then the {@link java.io.ObjectOutputStream#defaultWriteObject()}
+ * method of the given object-output stream is invoked. </p>
+ *
+ * @param os The object-output stream to which this object
+ * is to be written
+ */
+ private void writeObject(ObjectOutputStream os)
+ throws IOException
+ {
+ defineString();
+ os.defaultWriteObject(); // Writes the string field only
+ }
+
+ /**
+ * Reconstitutes a URI from the given serial stream.
+ *
+ * <p> The {@link java.io.ObjectInputStream#defaultReadObject()} method is
+ * invoked to read the value of the {@code string} field. The result is
+ * then parsed in the usual way.
+ *
+ * @param is The object-input stream from which this object
+ * is being read
+ */
+ private void readObject(ObjectInputStream is)
+ throws ClassNotFoundException, IOException
+ {
+ port = -1; // Argh
+ is.defaultReadObject();
+ try {
+ new Parser(string).parse(false);
+ } catch (URISyntaxException x) {
+ IOException y = new InvalidObjectException("Invalid URI");
+ y.initCause(x);
+ throw y;
+ }
+ }
+
+
+ // -- End of public methods --
+
+
+ // -- Utility methods for string-field comparison and hashing --
+
+ // These methods return appropriate values for null string arguments,
+ // thereby simplifying the equals, hashCode, and compareTo methods.
+ //
+ // The case-ignoring methods should only be applied to strings whose
+ // characters are all known to be US-ASCII. Because of this restriction,
+ // these methods are faster than the similar methods in the String class.
+
+ // US-ASCII only
+ private static int toLower(char c) {
+ if ((c >= 'A') && (c <= 'Z'))
+ return c + ('a' - 'A');
+ return c;
+ }
+
+ // US-ASCII only
+ private static int toUpper(char c) {
+ if ((c >= 'a') && (c <= 'z'))
+ return c - ('a' - 'A');
+ return c;
+ }
+
+ private static boolean equal(String s, String t) {
+ if (s == t) return true;
+ if ((s != null) && (t != null)) {
+ if (s.length() != t.length())
+ return false;
+ if (s.indexOf('%') < 0)
+ return s.equals(t);
+ int n = s.length();
+ for (int i = 0; i < n;) {
+ char c = s.charAt(i);
+ char d = t.charAt(i);
+ if (c != '%') {
+ if (c != d)
+ return false;
+ i++;
+ continue;
+ }
+ if (d != '%')
+ return false;
+ i++;
+ if (toLower(s.charAt(i)) != toLower(t.charAt(i)))
+ return false;
+ i++;
+ if (toLower(s.charAt(i)) != toLower(t.charAt(i)))
+ return false;
+ i++;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ // US-ASCII only
+ private static boolean equalIgnoringCase(String s, String t) {
+ if (s == t) return true;
+ if ((s != null) && (t != null)) {
+ int n = s.length();
+ if (t.length() != n)
+ return false;
+ for (int i = 0; i < n; i++) {
+ if (toLower(s.charAt(i)) != toLower(t.charAt(i)))
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private static int hash(int hash, String s) {
+ if (s == null) return hash;
+ return s.indexOf('%') < 0 ? hash * 127 + s.hashCode()
+ : normalizedHash(hash, s);
+ }
+
+
+ private static int normalizedHash(int hash, String s) {
+ int h = 0;
+ for (int index = 0; index < s.length(); index++) {
+ char ch = s.charAt(index);
+ h = 31 * h + ch;
+ if (ch == '%') {
+ /*
+ * Process the next two encoded characters
+ */
+ for (int i = index + 1; i < index + 3; i++)
+ h = 31 * h + toUpper(s.charAt(i));
+ index += 2;
+ }
+ }
+ return hash * 127 + h;
+ }
+
+ // US-ASCII only
+ private static int hashIgnoringCase(int hash, String s) {
+ if (s == null) return hash;
+ int h = hash;
+ int n = s.length();
+ for (int i = 0; i < n; i++)
+ h = 31 * h + toLower(s.charAt(i));
+ return h;
+ }
+
+ private static int compare(String s, String t) {
+ if (s == t) return 0;
+ if (s != null) {
+ if (t != null)
+ return s.compareTo(t);
+ else
+ return +1;
+ } else {
+ return -1;
+ }
+ }
+
+ // US-ASCII only
+ private static int compareIgnoringCase(String s, String t) {
+ if (s == t) return 0;
+ if (s != null) {
+ if (t != null) {
+ int sn = s.length();
+ int tn = t.length();
+ int n = sn < tn ? sn : tn;
+ for (int i = 0; i < n; i++) {
+ int c = toLower(s.charAt(i)) - toLower(t.charAt(i));
+ if (c != 0)
+ return c;
+ }
+ return sn - tn;
+ }
+ return +1;
+ } else {
+ return -1;
+ }
+ }
+
+
+ // -- String construction --
+
+ // If a scheme is given then the path, if given, must be absolute
+ //
+ private static void checkPath(String s, String scheme, String path)
+ throws URISyntaxException
+ {
+ if (scheme != null) {
+ if ((path != null)
+ && ((path.length() > 0) && (path.charAt(0) != '/')))
+ throw new URISyntaxException(s,
+ "Relative path in absolute URI");
+ }
+ }
+
+ private void appendAuthority(StringBuffer sb,
+ String authority,
+ String userInfo,
+ String host,
+ int port)
+ {
+ if (host != null) {
+ sb.append("//");
+ if (userInfo != null) {
+ sb.append(quote(userInfo, L_USERINFO, H_USERINFO));
+ sb.append('@');
+ }
+ boolean needBrackets = ((host.indexOf(':') >= 0)
+ && !host.startsWith("[")
+ && !host.endsWith("]"));
+ if (needBrackets) sb.append('[');
+ sb.append(host);
+ if (needBrackets) sb.append(']');
+ if (port != -1) {
+ sb.append(':');
+ sb.append(port);
+ }
+ } else if (authority != null) {
+ sb.append("//");
+ if (authority.startsWith("[")) {
+ // authority should (but may not) contain an embedded IPv6 address
+ int end = authority.indexOf("]");
+ String doquote = authority, dontquote = "";
+ if (end != -1 && authority.indexOf(":") != -1) {
+ // the authority contains an IPv6 address
+ if (end == authority.length()) {
+ dontquote = authority;
+ doquote = "";
+ } else {
+ dontquote = authority.substring(0 , end + 1);
+ doquote = authority.substring(end + 1);
+ }
+ }
+ sb.append(dontquote);
+ sb.append(quote(doquote,
+ L_REG_NAME | L_SERVER,
+ H_REG_NAME | H_SERVER));
+ } else {
+ sb.append(quote(authority,
+ L_REG_NAME | L_SERVER,
+ H_REG_NAME | H_SERVER));
+ }
+ }
+ }
+
+ private void appendSchemeSpecificPart(StringBuffer sb,
+ String opaquePart,
+ String authority,
+ String userInfo,
+ String host,
+ int port,
+ String path,
+ String query)
+ {
+ if (opaquePart != null) {
+ /* check if SSP begins with an IPv6 address
+ * because we must not quote a literal IPv6 address
+ */
+ if (opaquePart.startsWith("//[")) {
+ int end = opaquePart.indexOf("]");
+ if (end != -1 && opaquePart.indexOf(":")!=-1) {
+ String doquote, dontquote;
+ if (end == opaquePart.length()) {
+ dontquote = opaquePart;
+ doquote = "";
+ } else {
+ dontquote = opaquePart.substring(0,end+1);
+ doquote = opaquePart.substring(end+1);
+ }
+ sb.append (dontquote);
+ sb.append(quote(doquote, L_URIC, H_URIC));
+ }
+ } else {
+ sb.append(quote(opaquePart, L_URIC, H_URIC));
+ }
+ } else {
+ appendAuthority(sb, authority, userInfo, host, port);
+ if (path != null)
+ sb.append(quote(path, L_PATH, H_PATH));
+ if (query != null) {
+ sb.append('?');
+ sb.append(quote(query, L_URIC, H_URIC));
+ }
+ }
+ }
+
+ private void appendFragment(StringBuffer sb, String fragment) {
+ if (fragment != null) {
+ sb.append('#');
+ sb.append(quote(fragment, L_URIC, H_URIC));
+ }
+ }
+
+ private String toString(String scheme,
+ String opaquePart,
+ String authority,
+ String userInfo,
+ String host,
+ int port,
+ String path,
+ String query,
+ String fragment)
+ {
+ StringBuffer sb = new StringBuffer();
+ if (scheme != null) {
+ sb.append(scheme);
+ sb.append(':');
+ }
+ appendSchemeSpecificPart(sb, opaquePart,
+ authority, userInfo, host, port,
+ path, query);
+ appendFragment(sb, fragment);
+ return sb.toString();
+ }
+
+ private void defineSchemeSpecificPart() {
+ if (schemeSpecificPart != null) return;
+ StringBuffer sb = new StringBuffer();
+ appendSchemeSpecificPart(sb, null, getAuthority(), getUserInfo(),
+ host, port, getPath(), getQuery());
+ if (sb.length() == 0) return;
+ schemeSpecificPart = sb.toString();
+ }
+
+ private void defineString() {
+ if (string != null) return;
+
+ StringBuffer sb = new StringBuffer();
+ if (scheme != null) {
+ sb.append(scheme);
+ sb.append(':');
+ }
+ if (isOpaque()) {
+ sb.append(schemeSpecificPart);
+ } else {
+ if (host != null) {
+ sb.append("//");
+ if (userInfo != null) {
+ sb.append(userInfo);
+ sb.append('@');
+ }
+ boolean needBrackets = ((host.indexOf(':') >= 0)
+ && !host.startsWith("[")
+ && !host.endsWith("]"));
+ if (needBrackets) sb.append('[');
+ sb.append(host);
+ if (needBrackets) sb.append(']');
+ if (port != -1) {
+ sb.append(':');
+ sb.append(port);
+ }
+ } else if (authority != null) {
+ sb.append("//");
+ sb.append(authority);
+ }
+ if (path != null)
+ sb.append(path);
+ if (query != null) {
+ sb.append('?');
+ sb.append(query);
+ }
+ }
+ if (fragment != null) {
+ sb.append('#');
+ sb.append(fragment);
+ }
+ string = sb.toString();
+ }
+
+
+ // -- Normalization, resolution, and relativization --
+
+ // RFC2396 5.2 (6)
+ private static String resolvePath(String base, String child,
+ boolean absolute)
+ {
+ int i = base.lastIndexOf('/');
+ int cn = child.length();
+ String path = "";
+
+ if (cn == 0) {
+ // 5.2 (6a)
+ if (i >= 0)
+ path = base.substring(0, i + 1);
+ } else {
+ StringBuffer sb = new StringBuffer(base.length() + cn);
+ // 5.2 (6a)
+ if (i >= 0)
+ sb.append(base.substring(0, i + 1));
+ // 5.2 (6b)
+ sb.append(child);
+ path = sb.toString();
+ }
+
+ // 5.2 (6c-f)
+ // Android-changed: App compat. Remove leading dots when resolving path. http://b/25897693
+ // String np = normalize(path);
+ String np = normalize(path, true);
+
+ // 5.2 (6g): If the result is absolute but the path begins with "../",
+ // then we simply leave the path as-is
+
+ return np;
+ }
+
+ // RFC2396 5.2
+ private static URI resolve(URI base, URI child) {
+ // check if child if opaque first so that NPE is thrown
+ // if child is null.
+ if (child.isOpaque() || base.isOpaque())
+ return child;
+
+ // 5.2 (2): Reference to current document (lone fragment)
+ if ((child.scheme == null) && (child.authority == null)
+ && child.path.equals("") && (child.fragment != null)
+ && (child.query == null)) {
+ if ((base.fragment != null)
+ && child.fragment.equals(base.fragment)) {
+ return base;
+ }
+ URI ru = new URI();
+ ru.scheme = base.scheme;
+ ru.authority = base.authority;
+ ru.userInfo = base.userInfo;
+ ru.host = base.host;
+ ru.port = base.port;
+ ru.path = base.path;
+ ru.fragment = child.fragment;
+ ru.query = base.query;
+ return ru;
+ }
+
+ // 5.2 (3): Child is absolute
+ if (child.scheme != null)
+ return child;
+
+ URI ru = new URI(); // Resolved URI
+ ru.scheme = base.scheme;
+ ru.query = child.query;
+ ru.fragment = child.fragment;
+
+ // 5.2 (4): Authority
+ if (child.authority == null) {
+ ru.authority = base.authority;
+ ru.host = base.host;
+ ru.userInfo = base.userInfo;
+ ru.port = base.port;
+
+ // BEGIN Android-changed: App Compat. Handle null and empty path using RFC 3986 logic
+ // http://b/25897693
+ if (child.path == null || child.path.isEmpty()) {
+ // This is an additional path from RFC 3986 RI, which fixes following RFC 2396
+ // "normal" examples:
+ // Base: http://a/b/c/d;p?q
+ // "?y" = "http://a/b/c/d;p?y"
+ // "" = "http://a/b/c/d;p?q"
+ // http://b/25897693
+ ru.path = base.path;
+ ru.query = child.query != null ? child.query : base.query;
+ // END Android-changed: App Compat. Handle null and empty path using RFC 3986 logic
+ } else if ((child.path.length() > 0) && (child.path.charAt(0) == '/')) {
+ // 5.2 (5): Child path is absolute
+ //
+ // Android-changed: App Compat. Remove leading dots in path.
+ // There is an additional step from RFC 3986 RI, requiring to remove dots for
+ // absolute path as well.
+ // http://b/25897693
+ // ru.path = child.path;
+ ru.path = normalize(child.path, true);
+ } else {
+ // 5.2 (6): Resolve relative path
+ ru.path = resolvePath(base.path, child.path, base.isAbsolute());
+ }
+ } else {
+ ru.authority = child.authority;
+ ru.host = child.host;
+ ru.userInfo = child.userInfo;
+ ru.host = child.host;
+ ru.port = child.port;
+ ru.path = child.path;
+ }
+
+ // 5.2 (7): Recombine (nothing to do here)
+ return ru;
+ }
+
+ // If the given URI's path is normal then return the URI;
+ // o.w., return a new URI containing the normalized path.
+ //
+ private static URI normalize(URI u) {
+ if (u.isOpaque() || (u.path == null) || (u.path.length() == 0))
+ return u;
+
+ String np = normalize(u.path);
+ if (np == u.path)
+ return u;
+
+ URI v = new URI();
+ v.scheme = u.scheme;
+ v.fragment = u.fragment;
+ v.authority = u.authority;
+ v.userInfo = u.userInfo;
+ v.host = u.host;
+ v.port = u.port;
+ v.path = np;
+ v.query = u.query;
+ return v;
+ }
+
+ // If both URIs are hierarchical, their scheme and authority components are
+ // identical, and the base path is a prefix of the child's path, then
+ // return a relative URI that, when resolved against the base, yields the
+ // child; otherwise, return the child.
+ //
+ private static URI relativize(URI base, URI child) {
+ // check if child if opaque first so that NPE is thrown
+ // if child is null.
+ if (child.isOpaque() || base.isOpaque())
+ return child;
+ if (!equalIgnoringCase(base.scheme, child.scheme)
+ || !equal(base.authority, child.authority))
+ return child;
+
+ String bp = normalize(base.path);
+ String cp = normalize(child.path);
+ if (!bp.equals(cp)) {
+ // Android-changed: App Compat. Interpret ambiguous base path as a file, not a directory
+ // Upstream would append '/' to bp if not present, interpreting it as a directory; thus,
+ // /a/b/c relative to /a/b would become /c, whereas Android would relativize to /b/c.
+ // The spec is pretty vague about this but the Android behavior is kept because several
+ // tests enforce it.
+ // if (!bp.endsWith("/"))
+ // bp = bp + "/";
+ if (bp.indexOf('/') != -1) {
+ bp = bp.substring(0, bp.lastIndexOf('/') + 1);
+ }
+
+ if (!cp.startsWith(bp))
+ return child;
+ }
+
+ URI v = new URI();
+ v.path = cp.substring(bp.length());
+ v.query = child.query;
+ v.fragment = child.fragment;
+ return v;
+ }
+
+
+
+ // -- Path normalization --
+
+ // The following algorithm for path normalization avoids the creation of a
+ // string object for each segment, as well as the use of a string buffer to
+ // compute the final result, by using a single char array and editing it in
+ // place. The array is first split into segments, replacing each slash
+ // with '\0' and creating a segment-index array, each element of which is
+ // the index of the first char in the corresponding segment. We then walk
+ // through both arrays, removing ".", "..", and other segments as necessary
+ // by setting their entries in the index array to -1. Finally, the two
+ // arrays are used to rejoin the segments and compute the final result.
+ //
+ // This code is based upon src/solaris/native/java/io/canonicalize_md.c
+
+
+ // Check the given path to see if it might need normalization. A path
+ // might need normalization if it contains duplicate slashes, a "."
+ // segment, or a ".." segment. Return -1 if no further normalization is
+ // possible, otherwise return the number of segments found.
+ //
+ // This method takes a string argument rather than a char array so that
+ // this test can be performed without invoking path.toCharArray().
+ //
+ static private int needsNormalization(String path) {
+ boolean normal = true;
+ int ns = 0; // Number of segments
+ int end = path.length() - 1; // Index of last char in path
+ int p = 0; // Index of next char in path
+
+ // Skip initial slashes
+ while (p <= end) {
+ if (path.charAt(p) != '/') break;
+ p++;
+ }
+ if (p > 1) normal = false;
+
+ // Scan segments
+ while (p <= end) {
+
+ // Looking at "." or ".." ?
+ if ((path.charAt(p) == '.')
+ && ((p == end)
+ || ((path.charAt(p + 1) == '/')
+ || ((path.charAt(p + 1) == '.')
+ && ((p + 1 == end)
+ || (path.charAt(p + 2) == '/')))))) {
+ normal = false;
+ }
+ ns++;
+
+ // Find beginning of next segment
+ while (p <= end) {
+ if (path.charAt(p++) != '/')
+ continue;
+
+ // Skip redundant slashes
+ while (p <= end) {
+ if (path.charAt(p) != '/') break;
+ normal = false;
+ p++;
+ }
+
+ break;
+ }
+ }
+
+ return normal ? -1 : ns;
+ }
+
+
+ // Split the given path into segments, replacing slashes with nulls and
+ // filling in the given segment-index array.
+ //
+ // Preconditions:
+ // segs.length == Number of segments in path
+ //
+ // Postconditions:
+ // All slashes in path replaced by '\0'
+ // segs[i] == Index of first char in segment i (0 <= i < segs.length)
+ //
+ static private void split(char[] path, int[] segs) {
+ int end = path.length - 1; // Index of last char in path
+ int p = 0; // Index of next char in path
+ int i = 0; // Index of current segment
+
+ // Skip initial slashes
+ while (p <= end) {
+ if (path[p] != '/') break;
+ path[p] = '\0';
+ p++;
+ }
+
+ while (p <= end) {
+
+ // Note start of segment
+ segs[i++] = p++;
+
+ // Find beginning of next segment
+ while (p <= end) {
+ if (path[p++] != '/')
+ continue;
+ path[p - 1] = '\0';
+
+ // Skip redundant slashes
+ while (p <= end) {
+ if (path[p] != '/') break;
+ path[p++] = '\0';
+ }
+ break;
+ }
+ }
+
+ if (i != segs.length)
+ throw new InternalError(); // ASSERT
+ }
+
+
+ // Join the segments in the given path according to the given segment-index
+ // array, ignoring those segments whose index entries have been set to -1,
+ // and inserting slashes as needed. Return the length of the resulting
+ // path.
+ //
+ // Preconditions:
+ // segs[i] == -1 implies segment i is to be ignored
+ // path computed by split, as above, with '\0' having replaced '/'
+ //
+ // Postconditions:
+ // path[0] .. path[return value] == Resulting path
+ //
+ static private int join(char[] path, int[] segs) {
+ int ns = segs.length; // Number of segments
+ int end = path.length - 1; // Index of last char in path
+ int p = 0; // Index of next path char to write
+
+ if (path[p] == '\0') {
+ // Restore initial slash for absolute paths
+ path[p++] = '/';
+ }
+
+ for (int i = 0; i < ns; i++) {
+ int q = segs[i]; // Current segment
+ if (q == -1)
+ // Ignore this segment
+ continue;
+
+ if (p == q) {
+ // We're already at this segment, so just skip to its end
+ while ((p <= end) && (path[p] != '\0'))
+ p++;
+ if (p <= end) {
+ // Preserve trailing slash
+ path[p++] = '/';
+ }
+ } else if (p < q) {
+ // Copy q down to p
+ while ((q <= end) && (path[q] != '\0'))
+ path[p++] = path[q++];
+ if (q <= end) {
+ // Preserve trailing slash
+ path[p++] = '/';
+ }
+ } else
+ throw new InternalError(); // ASSERT false
+ }
+
+ return p;
+ }
+
+
+ // Remove "." segments from the given path, and remove segment pairs
+ // consisting of a non-".." segment followed by a ".." segment.
+ //
+ // Android-changed: App compat. Remove leading dots when resolving path. http://b/25897693
+ // private static void removeDots(char[] path, int[] segs) {
+ private static void removeDots(char[] path, int[] segs, boolean removeLeading) {
+ int ns = segs.length;
+ int end = path.length - 1;
+
+ for (int i = 0; i < ns; i++) {
+ int dots = 0; // Number of dots found (0, 1, or 2)
+
+ // Find next occurrence of "." or ".."
+ do {
+ int p = segs[i];
+ if (path[p] == '.') {
+ if (p == end) {
+ dots = 1;
+ break;
+ } else if (path[p + 1] == '\0') {
+ dots = 1;
+ break;
+ } else if ((path[p + 1] == '.')
+ && ((p + 1 == end)
+ || (path[p + 2] == '\0'))) {
+ dots = 2;
+ break;
+ }
+ }
+ i++;
+ } while (i < ns);
+ if ((i > ns) || (dots == 0))
+ break;
+
+ if (dots == 1) {
+ // Remove this occurrence of "."
+ segs[i] = -1;
+ } else {
+ // If there is a preceding non-".." segment, remove both that
+ // segment and this occurrence of ".."
+ int j;
+ for (j = i - 1; j >= 0; j--) {
+ if (segs[j] != -1) break;
+ }
+ if (j >= 0) {
+ int q = segs[j];
+ if (!((path[q] == '.')
+ && (path[q + 1] == '.')
+ && (path[q + 2] == '\0'))) {
+ segs[i] = -1;
+ segs[j] = -1;
+ }
+ // Android-added: App compat. Remove leading dots when resolving path.
+ // This is a leading ".." segment. Per RFC 3986 RI, this should be removed as
+ // well. This fixes RFC 2396 "abnormal" examples.
+ // http://b/25897693
+ } else if (removeLeading) {
+ segs[i] = -1;
+ }
+ }
+ }
+ }
+
+
+ // DEVIATION: If the normalized path is relative, and if the first
+ // segment could be parsed as a scheme name, then prepend a "." segment
+ //
+ private static void maybeAddLeadingDot(char[] path, int[] segs) {
+
+ if (path[0] == '\0')
+ // The path is absolute
+ return;
+
+ int ns = segs.length;
+ int f = 0; // Index of first segment
+ while (f < ns) {
+ if (segs[f] >= 0)
+ break;
+ f++;
+ }
+ if ((f >= ns) || (f == 0))
+ // The path is empty, or else the original first segment survived,
+ // in which case we already know that no leading "." is needed
+ return;
+
+ int p = segs[f];
+ while ((p < path.length) && (path[p] != ':') && (path[p] != '\0')) p++;
+ if (p >= path.length || path[p] == '\0')
+ // No colon in first segment, so no "." needed
+ return;
+
+ // At this point we know that the first segment is unused,
+ // hence we can insert a "." segment at that position
+ path[0] = '.';
+ path[1] = '\0';
+ segs[0] = 0;
+ }
+
+
+ // Normalize the given path string. A normal path string has no empty
+ // segments (i.e., occurrences of "//"), no segments equal to ".", and no
+ // segments equal to ".." that are preceded by a segment not equal to "..".
+ // In contrast to Unix-style pathname normalization, for URI paths we
+ // always retain trailing slashes.
+ //
+ private static String normalize(String ps) {
+ // BEGIN Android-changed: App compat. Remove leading dots when resolving path.
+ // Controlled by the "boolean removeLeading" argument added to normalize().
+ return normalize(ps, false);
+ }
+
+ private static String normalize(String ps, boolean removeLeading) {
+ // END Android-changed: App compat. Remove leading dots when resolving path.
+ // Does this path need normalization?
+ int ns = needsNormalization(ps); // Number of segments
+ if (ns < 0)
+ // Nope -- just return it
+ return ps;
+
+ char[] path = ps.toCharArray(); // Path in char-array form
+
+ // Split path into segments
+ int[] segs = new int[ns]; // Segment-index array
+ split(path, segs);
+
+ // Remove dots
+ // Android-changed: App compat. Remove leading dots when resolving path.
+ // removeDots(path, segs);
+ removeDots(path, segs, removeLeading);
+
+ // Prevent scheme-name confusion
+ maybeAddLeadingDot(path, segs);
+
+ // Join the remaining segments and return the result
+ String s = new String(path, 0, join(path, segs));
+ if (s.equals(ps)) {
+ // string was already normalized
+ return ps;
+ }
+ return s;
+ }
+
+
+
+ // -- Character classes for parsing --
+
+ // RFC2396 precisely specifies which characters in the US-ASCII charset are
+ // permissible in the various components of a URI reference. We here
+ // define a set of mask pairs to aid in enforcing these restrictions. Each
+ // mask pair consists of two longs, a low mask and a high mask. Taken
+ // together they represent a 128-bit mask, where bit i is set iff the
+ // character with value i is permitted.
+ //
+ // This approach is more efficient than sequentially searching arrays of
+ // permitted characters. It could be made still more efficient by
+ // precompiling the mask information so that a character's presence in a
+ // given mask could be determined by a single table lookup.
+
+ // Compute the low-order mask for the characters in the given string
+ private static long lowMask(String chars) {
+ int n = chars.length();
+ long m = 0;
+ for (int i = 0; i < n; i++) {
+ char c = chars.charAt(i);
+ if (c < 64)
+ m |= (1L << c);
+ }
+ return m;
+ }
+
+ // Compute the high-order mask for the characters in the given string
+ private static long highMask(String chars) {
+ int n = chars.length();
+ long m = 0;
+ for (int i = 0; i < n; i++) {
+ char c = chars.charAt(i);
+ if ((c >= 64) && (c < 128))
+ m |= (1L << (c - 64));
+ }
+ return m;
+ }
+
+ // Compute a low-order mask for the characters
+ // between first and last, inclusive
+ private static long lowMask(char first, char last) {
+ long m = 0;
+ int f = Math.max(Math.min(first, 63), 0);
+ int l = Math.max(Math.min(last, 63), 0);
+ for (int i = f; i <= l; i++)
+ m |= 1L << i;
+ return m;
+ }
+
+ // Compute a high-order mask for the characters
+ // between first and last, inclusive
+ private static long highMask(char first, char last) {
+ long m = 0;
+ int f = Math.max(Math.min(first, 127), 64) - 64;
+ int l = Math.max(Math.min(last, 127), 64) - 64;
+ for (int i = f; i <= l; i++)
+ m |= 1L << i;
+ return m;
+ }
+
+ // Tell whether the given character is permitted by the given mask pair
+ private static boolean match(char c, long lowMask, long highMask) {
+ if (c == 0) // 0 doesn't have a slot in the mask. So, it never matches.
+ return false;
+ if (c < 64)
+ return ((1L << c) & lowMask) != 0;
+ if (c < 128)
+ return ((1L << (c - 64)) & highMask) != 0;
+ return false;
+ }
+
+ // Character-class masks, in reverse order from RFC2396 because
+ // initializers for static fields cannot make forward references.
+
+ // digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
+ // "8" | "9"
+ private static final long L_DIGIT = lowMask('0', '9');
+ private static final long H_DIGIT = 0L;
+
+ // upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
+ // "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
+ // "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
+ private static final long L_UPALPHA = 0L;
+ private static final long H_UPALPHA = highMask('A', 'Z');
+
+ // lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
+ // "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
+ // "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
+ private static final long L_LOWALPHA = 0L;
+ private static final long H_LOWALPHA = highMask('a', 'z');
+
+ // alpha = lowalpha | upalpha
+ private static final long L_ALPHA = L_LOWALPHA | L_UPALPHA;
+ private static final long H_ALPHA = H_LOWALPHA | H_UPALPHA;
+
+ // alphanum = alpha | digit
+ private static final long L_ALPHANUM = L_DIGIT | L_ALPHA;
+ private static final long H_ALPHANUM = H_DIGIT | H_ALPHA;
+
+ // hex = digit | "A" | "B" | "C" | "D" | "E" | "F" |
+ // "a" | "b" | "c" | "d" | "e" | "f"
+ private static final long L_HEX = L_DIGIT;
+ private static final long H_HEX = highMask('A', 'F') | highMask('a', 'f');
+
+ // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" |
+ // "(" | ")"
+ private static final long L_MARK = lowMask("-_.!~*'()");
+ private static final long H_MARK = highMask("-_.!~*'()");
+
+ // unreserved = alphanum | mark
+ private static final long L_UNRESERVED = L_ALPHANUM | L_MARK;
+ private static final long H_UNRESERVED = H_ALPHANUM | H_MARK;
+
+ // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
+ // "$" | "," | "[" | "]"
+ // Added per RFC2732: "[", "]"
+ private static final long L_RESERVED = lowMask(";/?:@&=+$,[]");
+ private static final long H_RESERVED = highMask(";/?:@&=+$,[]");
+
+ // The zero'th bit is used to indicate that escape pairs and non-US-ASCII
+ // characters are allowed; this is handled by the scanEscape method below.
+ private static final long L_ESCAPED = 1L;
+ private static final long H_ESCAPED = 0L;
+
+ // uric = reserved | unreserved | escaped
+ private static final long L_URIC = L_RESERVED | L_UNRESERVED | L_ESCAPED;
+ private static final long H_URIC = H_RESERVED | H_UNRESERVED | H_ESCAPED;
+
+ // pchar = unreserved | escaped |
+ // ":" | "@" | "&" | "=" | "+" | "$" | ","
+ private static final long L_PCHAR
+ = L_UNRESERVED | L_ESCAPED | lowMask(":@&=+$,");
+ private static final long H_PCHAR
+ = H_UNRESERVED | H_ESCAPED | highMask(":@&=+$,");
+
+ // All valid path characters
+ private static final long L_PATH = L_PCHAR | lowMask(";/");
+ private static final long H_PATH = H_PCHAR | highMask(";/");
+
+ // Dash, for use in domainlabel and toplabel
+ private static final long L_DASH = lowMask("-");
+ private static final long H_DASH = highMask("-");
+
+ // BEGIN Android-added: Allow underscore in hostname.
+ // UNDERSCORE, for use in domainlabel and toplabel
+ private static final long L_UNDERSCORE = lowMask("_");
+ private static final long H_UNDERSCORE = highMask("_");
+ // END Android-added: Allow underscore in hostname.
+
+ // Dot, for use in hostnames
+ private static final long L_DOT = lowMask(".");
+ private static final long H_DOT = highMask(".");
+
+ // userinfo = *( unreserved | escaped |
+ // ";" | ":" | "&" | "=" | "+" | "$" | "," )
+ private static final long L_USERINFO
+ = L_UNRESERVED | L_ESCAPED | lowMask(";:&=+$,");
+ private static final long H_USERINFO
+ = H_UNRESERVED | H_ESCAPED | highMask(";:&=+$,");
+
+ // reg_name = 1*( unreserved | escaped | "$" | "," |
+ // ";" | ":" | "@" | "&" | "=" | "+" )
+ private static final long L_REG_NAME
+ = L_UNRESERVED | L_ESCAPED | lowMask("$,;:@&=+");
+ private static final long H_REG_NAME
+ = H_UNRESERVED | H_ESCAPED | highMask("$,;:@&=+");
+
+ // All valid characters for server-based authorities
+ private static final long L_SERVER
+ = L_USERINFO | L_ALPHANUM | L_DASH | lowMask(".:@[]");
+ private static final long H_SERVER
+ = H_USERINFO | H_ALPHANUM | H_DASH | highMask(".:@[]");
+
+ // Special case of server authority that represents an IPv6 address
+ // In this case, a % does not signify an escape sequence
+ private static final long L_SERVER_PERCENT
+ = L_SERVER | lowMask("%");
+ private static final long H_SERVER_PERCENT
+ = H_SERVER | highMask("%");
+ private static final long L_LEFT_BRACKET = lowMask("[");
+ private static final long H_LEFT_BRACKET = highMask("[");
+
+ // scheme = alpha *( alpha | digit | "+" | "-" | "." )
+ private static final long L_SCHEME = L_ALPHA | L_DIGIT | lowMask("+-.");
+ private static final long H_SCHEME = H_ALPHA | H_DIGIT | highMask("+-.");
+
+ // uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" |
+ // "&" | "=" | "+" | "$" | ","
+ private static final long L_URIC_NO_SLASH
+ = L_UNRESERVED | L_ESCAPED | lowMask(";?:@&=+$,");
+ private static final long H_URIC_NO_SLASH
+ = H_UNRESERVED | H_ESCAPED | highMask(";?:@&=+$,");
+
+
+ // -- Escaping and encoding --
+
+ private final static char[] hexDigits = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
+ private static void appendEscape(StringBuffer sb, byte b) {
+ sb.append('%');
+ sb.append(hexDigits[(b >> 4) & 0x0f]);
+ sb.append(hexDigits[(b >> 0) & 0x0f]);
+ }
+
+ private static void appendEncoded(StringBuffer sb, char c) {
+ ByteBuffer bb = null;
+ try {
+ bb = ThreadLocalCoders.encoderFor("UTF-8")
+ .encode(CharBuffer.wrap("" + c));
+ } catch (CharacterCodingException x) {
+ assert false;
+ }
+ while (bb.hasRemaining()) {
+ int b = bb.get() & 0xff;
+ if (b >= 0x80)
+ appendEscape(sb, (byte)b);
+ else
+ sb.append((char)b);
+ }
+ }
+
+ // Quote any characters in s that are not permitted
+ // by the given mask pair
+ //
+ private static String quote(String s, long lowMask, long highMask) {
+ int n = s.length();
+ StringBuffer sb = null;
+ boolean allowNonASCII = ((lowMask & L_ESCAPED) != 0);
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (c < '\u0080') {
+ if (!match(c, lowMask, highMask)) {
+ if (sb == null) {
+ sb = new StringBuffer();
+ sb.append(s.substring(0, i));
+ }
+ appendEscape(sb, (byte)c);
+ } else {
+ if (sb != null)
+ sb.append(c);
+ }
+ } else if (allowNonASCII
+ && (Character.isSpaceChar(c)
+ || Character.isISOControl(c))) {
+ if (sb == null) {
+ sb = new StringBuffer();
+ sb.append(s.substring(0, i));
+ }
+ appendEncoded(sb, c);
+ } else {
+ if (sb != null)
+ sb.append(c);
+ }
+ }
+ return (sb == null) ? s : sb.toString();
+ }
+
+ // Encodes all characters >= \u0080 into escaped, normalized UTF-8 octets,
+ // assuming that s is otherwise legal
+ //
+ private static String encode(String s) {
+ int n = s.length();
+ if (n == 0)
+ return s;
+
+ // First check whether we actually need to encode
+ for (int i = 0;;) {
+ if (s.charAt(i) >= '\u0080')
+ break;
+ if (++i >= n)
+ return s;
+ }
+
+ String ns = Normalizer.normalize(s, Normalizer.Form.NFC);
+ ByteBuffer bb = null;
+ try {
+ bb = ThreadLocalCoders.encoderFor("UTF-8")
+ .encode(CharBuffer.wrap(ns));
+ } catch (CharacterCodingException x) {
+ assert false;
+ }
+
+ StringBuffer sb = new StringBuffer();
+ while (bb.hasRemaining()) {
+ int b = bb.get() & 0xff;
+ if (b >= 0x80)
+ appendEscape(sb, (byte)b);
+ else
+ sb.append((char)b);
+ }
+ return sb.toString();
+ }
+
+ private static int decode(char c) {
+ if ((c >= '0') && (c <= '9'))
+ return c - '0';
+ if ((c >= 'a') && (c <= 'f'))
+ return c - 'a' + 10;
+ if ((c >= 'A') && (c <= 'F'))
+ return c - 'A' + 10;
+ assert false;
+ return -1;
+ }
+
+ private static byte decode(char c1, char c2) {
+ return (byte)( ((decode(c1) & 0xf) << 4)
+ | ((decode(c2) & 0xf) << 0));
+ }
+
+ // Evaluates all escapes in s, applying UTF-8 decoding if needed. Assumes
+ // that escapes are well-formed syntactically, i.e., of the form %XX. If a
+ // sequence of escaped octets is not valid UTF-8 then the erroneous octets
+ // are replaced with '\uFFFD'.
+ // Exception: any "%" found between "[]" is left alone. It is an IPv6 literal
+ // with a scope_id
+ //
+ private static String decode(String s) {
+ if (s == null)
+ return s;
+ int n = s.length();
+ if (n == 0)
+ return s;
+ if (s.indexOf('%') < 0)
+ return s;
+
+ StringBuffer sb = new StringBuffer(n);
+ ByteBuffer bb = ByteBuffer.allocate(n);
+ CharBuffer cb = CharBuffer.allocate(n);
+ CharsetDecoder dec = ThreadLocalCoders.decoderFor("UTF-8")
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE);
+
+ // This is not horribly efficient, but it will do for now
+ char c = s.charAt(0);
+ boolean betweenBrackets = false;
+
+ for (int i = 0; i < n;) {
+ assert c == s.charAt(i); // Loop invariant
+ if (c == '[') {
+ betweenBrackets = true;
+ } else if (betweenBrackets && c == ']') {
+ betweenBrackets = false;
+ }
+ if (c != '%' || betweenBrackets) {
+ sb.append(c);
+ if (++i >= n)
+ break;
+ c = s.charAt(i);
+ continue;
+ }
+ bb.clear();
+ int ui = i;
+ for (;;) {
+ assert (n - i >= 2);
+ bb.put(decode(s.charAt(++i), s.charAt(++i)));
+ if (++i >= n)
+ break;
+ c = s.charAt(i);
+ if (c != '%')
+ break;
+ }
+ bb.flip();
+ cb.clear();
+ dec.reset();
+ CoderResult cr = dec.decode(bb, cb, true);
+ assert cr.isUnderflow();
+ cr = dec.flush(cb);
+ assert cr.isUnderflow();
+ sb.append(cb.flip().toString());
+ }
+
+ return sb.toString();
+ }
+
+
+ // -- Parsing --
+
+ // For convenience we wrap the input URI string in a new instance of the
+ // following internal class. This saves always having to pass the input
+ // string as an argument to each internal scan/parse method.
+
+ private class Parser {
+
+ private String input; // URI input string
+ private boolean requireServerAuthority = false;
+
+ Parser(String s) {
+ input = s;
+ string = s;
+ }
+
+ // -- Methods for throwing URISyntaxException in various ways --
+
+ private void fail(String reason) throws URISyntaxException {
+ throw new URISyntaxException(input, reason);
+ }
+
+ private void fail(String reason, int p) throws URISyntaxException {
+ throw new URISyntaxException(input, reason, p);
+ }
+
+ private void failExpecting(String expected, int p)
+ throws URISyntaxException
+ {
+ fail("Expected " + expected, p);
+ }
+
+ private void failExpecting(String expected, String prior, int p)
+ throws URISyntaxException
+ {
+ fail("Expected " + expected + " following " + prior, p);
+ }
+
+
+ // -- Simple access to the input string --
+
+ // Return a substring of the input string
+ //
+ private String substring(int start, int end) {
+ return input.substring(start, end);
+ }
+
+ // Return the char at position p,
+ // assuming that p < input.length()
+ //
+ private char charAt(int p) {
+ return input.charAt(p);
+ }
+
+ // Tells whether start < end and, if so, whether charAt(start) == c
+ //
+ private boolean at(int start, int end, char c) {
+ return (start < end) && (charAt(start) == c);
+ }
+
+ // Tells whether start + s.length() < end and, if so,
+ // whether the chars at the start position match s exactly
+ //
+ private boolean at(int start, int end, String s) {
+ int p = start;
+ int sn = s.length();
+ if (sn > end - p)
+ return false;
+ int i = 0;
+ while (i < sn) {
+ if (charAt(p++) != s.charAt(i)) {
+ break;
+ }
+ i++;
+ }
+ return (i == sn);
+ }
+
+
+ // -- Scanning --
+
+ // The various scan and parse methods that follow use a uniform
+ // convention of taking the current start position and end index as
+ // their first two arguments. The start is inclusive while the end is
+ // exclusive, just as in the String class, i.e., a start/end pair
+ // denotes the left-open interval [start, end) of the input string.
+ //
+ // These methods never proceed past the end position. They may return
+ // -1 to indicate outright failure, but more often they simply return
+ // the position of the first char after the last char scanned. Thus
+ // a typical idiom is
+ //
+ // int p = start;
+ // int q = scan(p, end, ...);
+ // if (q > p)
+ // // We scanned something
+ // ...;
+ // else if (q == p)
+ // // We scanned nothing
+ // ...;
+ // else if (q == -1)
+ // // Something went wrong
+ // ...;
+
+
+ // Scan a specific char: If the char at the given start position is
+ // equal to c, return the index of the next char; otherwise, return the
+ // start position.
+ //
+ private int scan(int start, int end, char c) {
+ if ((start < end) && (charAt(start) == c))
+ return start + 1;
+ return start;
+ }
+
+ // Scan forward from the given start position. Stop at the first char
+ // in the err string (in which case -1 is returned), or the first char
+ // in the stop string (in which case the index of the preceding char is
+ // returned), or the end of the input string (in which case the length
+ // of the input string is returned). May return the start position if
+ // nothing matches.
+ //
+ private int scan(int start, int end, String err, String stop) {
+ int p = start;
+ while (p < end) {
+ char c = charAt(p);
+ if (err.indexOf(c) >= 0)
+ return -1;
+ if (stop.indexOf(c) >= 0)
+ break;
+ p++;
+ }
+ return p;
+ }
+
+ // Scan a potential escape sequence, starting at the given position,
+ // with the given first char (i.e., charAt(start) == c).
+ //
+ // This method assumes that if escapes are allowed then visible
+ // non-US-ASCII chars are also allowed.
+ //
+ private int scanEscape(int start, int n, char first)
+ throws URISyntaxException
+ {
+ int p = start;
+ char c = first;
+ if (c == '%') {
+ // Process escape pair
+ if ((p + 3 <= n)
+ && match(charAt(p + 1), L_HEX, H_HEX)
+ && match(charAt(p + 2), L_HEX, H_HEX)) {
+ return p + 3;
+ }
+ fail("Malformed escape pair", p);
+ } else if ((c > 128)
+ && !Character.isSpaceChar(c)
+ && !Character.isISOControl(c)) {
+ // Allow unescaped but visible non-US-ASCII chars
+ return p + 1;
+ }
+ return p;
+ }
+
+ // Scan chars that match the given mask pair
+ //
+ private int scan(int start, int n, long lowMask, long highMask)
+ throws URISyntaxException
+ {
+ int p = start;
+ while (p < n) {
+ char c = charAt(p);
+ if (match(c, lowMask, highMask)) {
+ p++;
+ continue;
+ }
+ if ((lowMask & L_ESCAPED) != 0) {
+ int q = scanEscape(p, n, c);
+ if (q > p) {
+ p = q;
+ continue;
+ }
+ }
+ break;
+ }
+ return p;
+ }
+
+ // Check that each of the chars in [start, end) matches the given mask
+ //
+ private void checkChars(int start, int end,
+ long lowMask, long highMask,
+ String what)
+ throws URISyntaxException
+ {
+ int p = scan(start, end, lowMask, highMask);
+ if (p < end)
+ fail("Illegal character in " + what, p);
+ }
+
+ // Check that the char at position p matches the given mask
+ //
+ private void checkChar(int p,
+ long lowMask, long highMask,
+ String what)
+ throws URISyntaxException
+ {
+ checkChars(p, p + 1, lowMask, highMask, what);
+ }
+
+
+ // -- Parsing --
+
+ // [<scheme>:]<scheme-specific-part>[#<fragment>]
+ //
+ void parse(boolean rsa) throws URISyntaxException {
+ requireServerAuthority = rsa;
+ int ssp; // Start of scheme-specific part
+ int n = input.length();
+ int p = scan(0, n, "/?#", ":");
+ if ((p >= 0) && at(p, n, ':')) {
+ if (p == 0)
+ failExpecting("scheme name", 0);
+ checkChar(0, L_ALPHA, H_ALPHA, "scheme name");
+ checkChars(1, p, L_SCHEME, H_SCHEME, "scheme name");
+ scheme = substring(0, p);
+ p++; // Skip ':'
+ ssp = p;
+ if (at(p, n, '/')) {
+ p = parseHierarchical(p, n);
+ } else {
+ int q = scan(p, n, "", "#");
+ if (q <= p)
+ failExpecting("scheme-specific part", p);
+ checkChars(p, q, L_URIC, H_URIC, "opaque part");
+ p = q;
+ }
+ } else {
+ ssp = 0;
+ p = parseHierarchical(0, n);
+ }
+ schemeSpecificPart = substring(ssp, p);
+ if (at(p, n, '#')) {
+ checkChars(p + 1, n, L_URIC, H_URIC, "fragment");
+ fragment = substring(p + 1, n);
+ p = n;
+ }
+ if (p < n)
+ fail("end of URI", p);
+ }
+
+ // [//authority]<path>[?<query>]
+ //
+ // DEVIATION from RFC2396: We allow an empty authority component as
+ // long as it's followed by a non-empty path, query component, or
+ // fragment component. This is so that URIs such as "file:///foo/bar"
+ // will parse. This seems to be the intent of RFC2396, though the
+ // grammar does not permit it. If the authority is empty then the
+ // userInfo, host, and port components are undefined.
+ //
+ // DEVIATION from RFC2396: We allow empty relative paths. This seems
+ // to be the intent of RFC2396, but the grammar does not permit it.
+ // The primary consequence of this deviation is that "#f" parses as a
+ // relative URI with an empty path.
+ //
+ private int parseHierarchical(int start, int n)
+ throws URISyntaxException
+ {
+ int p = start;
+ if (at(p, n, '/') && at(p + 1, n, '/')) {
+ p += 2;
+ int q = scan(p, n, "", "/?#");
+ if (q > p) {
+ p = parseAuthority(p, q);
+ } else if (q < n) {
+ // DEVIATION: Allow empty authority prior to non-empty
+ // path, query component or fragment identifier
+ } else
+ failExpecting("authority", p);
+ }
+ int q = scan(p, n, "", "?#"); // DEVIATION: May be empty
+ checkChars(p, q, L_PATH, H_PATH, "path");
+ path = substring(p, q);
+ p = q;
+ if (at(p, n, '?')) {
+ p++;
+ q = scan(p, n, "", "#");
+ checkChars(p, q, L_URIC, H_URIC, "query");
+ query = substring(p, q);
+ p = q;
+ }
+ return p;
+ }
+
+ // authority = server | reg_name
+ //
+ // Ambiguity: An authority that is a registry name rather than a server
+ // might have a prefix that parses as a server. We use the fact that
+ // the authority component is always followed by '/' or the end of the
+ // input string to resolve this: If the complete authority did not
+ // parse as a server then we try to parse it as a registry name.
+ //
+ private int parseAuthority(int start, int n)
+ throws URISyntaxException
+ {
+ int p = start;
+ int q = p;
+ URISyntaxException ex = null;
+
+ boolean serverChars;
+ boolean regChars;
+
+ if (scan(p, n, "", "]") > p) {
+ // contains a literal IPv6 address, therefore % is allowed
+ serverChars = (scan(p, n, L_SERVER_PERCENT, H_SERVER_PERCENT) == n);
+ } else {
+ serverChars = (scan(p, n, L_SERVER, H_SERVER) == n);
+ }
+ regChars = (scan(p, n, L_REG_NAME, H_REG_NAME) == n);
+
+ if (regChars && !serverChars) {
+ // Must be a registry-based authority
+ authority = substring(p, n);
+ return n;
+ }
+
+ if (serverChars) {
+ // Might be (probably is) a server-based authority, so attempt
+ // to parse it as such. If the attempt fails, try to treat it
+ // as a registry-based authority.
+ try {
+ q = parseServer(p, n);
+ if (q < n)
+ failExpecting("end of authority", q);
+ authority = substring(p, n);
+ } catch (URISyntaxException x) {
+ // Undo results of failed parse
+ userInfo = null;
+ host = null;
+ port = -1;
+ if (requireServerAuthority) {
+ // If we're insisting upon a server-based authority,
+ // then just re-throw the exception
+ throw x;
+ } else {
+ // Save the exception in case it doesn't parse as a
+ // registry either
+ ex = x;
+ q = p;
+ }
+ }
+ }
+
+ if (q < n) {
+ if (regChars) {
+ // Registry-based authority
+ authority = substring(p, n);
+ } else if (ex != null) {
+ // Re-throw exception; it was probably due to
+ // a malformed IPv6 address
+ throw ex;
+ } else {
+ fail("Illegal character in authority", q);
+ }
+ }
+
+ return n;
+ }
+
+
+ // [<userinfo>@]<host>[:<port>]
+ //
+ private int parseServer(int start, int n)
+ throws URISyntaxException
+ {
+ int p = start;
+ int q;
+
+ // userinfo
+ q = scan(p, n, "/?#", "@");
+ if ((q >= p) && at(q, n, '@')) {
+ checkChars(p, q, L_USERINFO, H_USERINFO, "user info");
+ userInfo = substring(p, q);
+ p = q + 1; // Skip '@'
+ }
+
+ // hostname, IPv4 address, or IPv6 address
+ if (at(p, n, '[')) {
+ // DEVIATION from RFC2396: Support IPv6 addresses, per RFC2732
+ p++;
+ q = scan(p, n, "/?#", "]");
+ if ((q > p) && at(q, n, ']')) {
+ // look for a "%" scope id
+ int r = scan (p, q, "", "%");
+ if (r > p) {
+ parseIPv6Reference(p, r);
+ if (r+1 == q) {
+ fail ("scope id expected");
+ }
+ checkChars (r+1, q, L_ALPHANUM, H_ALPHANUM,
+ "scope id");
+ } else {
+ parseIPv6Reference(p, q);
+ }
+ host = substring(p-1, q+1);
+ p = q + 1;
+ } else {
+ failExpecting("closing bracket for IPv6 address", q);
+ }
+ } else {
+ q = parseIPv4Address(p, n);
+ if (q <= p)
+ q = parseHostname(p, n);
+ p = q;
+ }
+
+ // port
+ if (at(p, n, ':')) {
+ p++;
+ q = scan(p, n, "", "/");
+ if (q > p) {
+ checkChars(p, q, L_DIGIT, H_DIGIT, "port number");
+ try {
+ port = Integer.parseInt(substring(p, q));
+ } catch (NumberFormatException x) {
+ fail("Malformed port number", p);
+ }
+ p = q;
+ }
+ }
+ if (p < n)
+ failExpecting("port number", p);
+
+ return p;
+ }
+
+ // Scan a string of decimal digits whose value fits in a byte
+ //
+ private int scanByte(int start, int n)
+ throws URISyntaxException
+ {
+ int p = start;
+ int q = scan(p, n, L_DIGIT, H_DIGIT);
+ if (q <= p) return q;
+ if (Integer.parseInt(substring(p, q)) > 255) return p;
+ return q;
+ }
+
+ // Scan an IPv4 address.
+ //
+ // If the strict argument is true then we require that the given
+ // interval contain nothing besides an IPv4 address; if it is false
+ // then we only require that it start with an IPv4 address.
+ //
+ // If the interval does not contain or start with (depending upon the
+ // strict argument) a legal IPv4 address characters then we return -1
+ // immediately; otherwise we insist that these characters parse as a
+ // legal IPv4 address and throw an exception on failure.
+ //
+ // We assume that any string of decimal digits and dots must be an IPv4
+ // address. It won't parse as a hostname anyway, so making that
+ // assumption here allows more meaningful exceptions to be thrown.
+ //
+ private int scanIPv4Address(int start, int n, boolean strict)
+ throws URISyntaxException
+ {
+ int p = start;
+ int q;
+ int m = scan(p, n, L_DIGIT | L_DOT, H_DIGIT | H_DOT);
+ if ((m <= p) || (strict && (m != n)))
+ return -1;
+ for (;;) {
+ // Per RFC2732: At most three digits per byte
+ // Further constraint: Each element fits in a byte
+ if ((q = scanByte(p, m)) <= p) break; p = q;
+ if ((q = scan(p, m, '.')) <= p) break; p = q;
+ if ((q = scanByte(p, m)) <= p) break; p = q;
+ if ((q = scan(p, m, '.')) <= p) break; p = q;
+ if ((q = scanByte(p, m)) <= p) break; p = q;
+ if ((q = scan(p, m, '.')) <= p) break; p = q;
+ if ((q = scanByte(p, m)) <= p) break; p = q;
+ if (q < m) break;
+ return q;
+ }
+ fail("Malformed IPv4 address", q);
+ return -1;
+ }
+
+ // Take an IPv4 address: Throw an exception if the given interval
+ // contains anything except an IPv4 address
+ //
+ private int takeIPv4Address(int start, int n, String expected)
+ throws URISyntaxException
+ {
+ int p = scanIPv4Address(start, n, true);
+ if (p <= start)
+ failExpecting(expected, start);
+ return p;
+ }
+
+ // Attempt to parse an IPv4 address, returning -1 on failure but
+ // allowing the given interval to contain [:<characters>] after
+ // the IPv4 address.
+ //
+ private int parseIPv4Address(int start, int n) {
+ int p;
+
+ try {
+ p = scanIPv4Address(start, n, false);
+ } catch (URISyntaxException x) {
+ return -1;
+ } catch (NumberFormatException nfe) {
+ return -1;
+ }
+
+ if (p > start && p < n) {
+ // IPv4 address is followed by something - check that
+ // it's a ":" as this is the only valid character to
+ // follow an address.
+ if (charAt(p) != ':') {
+ p = -1;
+ }
+ }
+
+ if (p > start)
+ host = substring(start, p);
+
+ return p;
+ }
+
+ // Android-changed: Allow underscore in hostname.
+ // Added "_" to the grammars for domainLabel and topLabel.
+ // hostname = domainlabel [ "." ] | 1*( domainlabel "." ) toplabel [ "." ]
+ // domainlabel = alphanum | alphanum *( alphanum | "-" | "_" ) alphanum
+ // toplabel = alpha | alpha *( alphanum | "-" | "_" ) alphanum
+ //
+ private int parseHostname(int start, int n)
+ throws URISyntaxException
+ {
+ int p = start;
+ int q;
+ int l = -1; // Start of last parsed label
+
+ do {
+ // Android-changed: Allow underscore in hostname.
+ // RFC 2396 only allows alphanumeric characters and hyphens, but real,
+ // large Internet hosts in the wild use underscore, so we have to allow it.
+ // http://code.google.com/p/android/issues/detail?id=37577
+ // http://b/17579865
+ // http://b/18016625
+ // http://b/18023709
+
+ // domainlabel = alphanum [ *( alphanum | "-" | "_" ) alphanum ]
+ q = scan(p, n, L_ALPHANUM, H_ALPHANUM);
+ if (q <= p)
+ break;
+ l = p;
+ if (q > p) {
+ p = q;
+ // Android-changed: Allow underscore in hostname.
+ // q = scan(p, n, L_ALPHANUM | L_DASH, H_ALPHANUM | H_DASH);
+ q = scan(p, n, L_ALPHANUM | L_DASH | L_UNDERSCORE, H_ALPHANUM | H_DASH | H_UNDERSCORE);
+ if (q > p) {
+ if (charAt(q - 1) == '-')
+ fail("Illegal character in hostname", q - 1);
+ p = q;
+ }
+ }
+ q = scan(p, n, '.');
+ if (q <= p)
+ break;
+ p = q;
+ } while (p < n);
+
+ if ((p < n) && !at(p, n, ':'))
+ fail("Illegal character in hostname", p);
+
+ if (l < 0)
+ failExpecting("hostname", start);
+
+ // for a fully qualified hostname check that the rightmost
+ // label starts with an alpha character.
+ if (l > start && !match(charAt(l), L_ALPHA, H_ALPHA)) {
+ fail("Illegal character in hostname", l);
+ }
+
+ host = substring(start, p);
+ return p;
+ }
+
+
+ // IPv6 address parsing, from RFC2373: IPv6 Addressing Architecture
+ //
+ // Bug: The grammar in RFC2373 Appendix B does not allow addresses of
+ // the form ::12.34.56.78, which are clearly shown in the examples
+ // earlier in the document. Here is the original grammar:
+ //
+ // IPv6address = hexpart [ ":" IPv4address ]
+ // hexpart = hexseq | hexseq "::" [ hexseq ] | "::" [ hexseq ]
+ // hexseq = hex4 *( ":" hex4)
+ // hex4 = 1*4HEXDIG
+ //
+ // We therefore use the following revised grammar:
+ //
+ // IPv6address = hexseq [ ":" IPv4address ]
+ // | hexseq [ "::" [ hexpost ] ]
+ // | "::" [ hexpost ]
+ // hexpost = hexseq | hexseq ":" IPv4address | IPv4address
+ // hexseq = hex4 *( ":" hex4)
+ // hex4 = 1*4HEXDIG
+ //
+ // This covers all and only the following cases:
+ //
+ // hexseq
+ // hexseq : IPv4address
+ // hexseq ::
+ // hexseq :: hexseq
+ // hexseq :: hexseq : IPv4address
+ // hexseq :: IPv4address
+ // :: hexseq
+ // :: hexseq : IPv4address
+ // :: IPv4address
+ // ::
+ //
+ // Additionally we constrain the IPv6 address as follows :-
+ //
+ // i. IPv6 addresses without compressed zeros should contain
+ // exactly 16 bytes.
+ //
+ // ii. IPv6 addresses with compressed zeros should contain
+ // less than 16 bytes.
+
+ private int ipv6byteCount = 0;
+
+ private int parseIPv6Reference(int start, int n)
+ throws URISyntaxException
+ {
+ int p = start;
+ int q;
+ boolean compressedZeros = false;
+
+ q = scanHexSeq(p, n);
+
+ if (q > p) {
+ p = q;
+ if (at(p, n, "::")) {
+ compressedZeros = true;
+ p = scanHexPost(p + 2, n);
+ } else if (at(p, n, ':')) {
+ p = takeIPv4Address(p + 1, n, "IPv4 address");
+ ipv6byteCount += 4;
+ }
+ } else if (at(p, n, "::")) {
+ compressedZeros = true;
+ p = scanHexPost(p + 2, n);
+ }
+ if (p < n)
+ fail("Malformed IPv6 address", start);
+ if (ipv6byteCount > 16)
+ fail("IPv6 address too long", start);
+ if (!compressedZeros && ipv6byteCount < 16)
+ fail("IPv6 address too short", start);
+ if (compressedZeros && ipv6byteCount == 16)
+ fail("Malformed IPv6 address", start);
+
+ return p;
+ }
+
+ private int scanHexPost(int start, int n)
+ throws URISyntaxException
+ {
+ int p = start;
+ int q;
+
+ if (p == n)
+ return p;
+
+ q = scanHexSeq(p, n);
+ if (q > p) {
+ p = q;
+ if (at(p, n, ':')) {
+ p++;
+ p = takeIPv4Address(p, n, "hex digits or IPv4 address");
+ ipv6byteCount += 4;
+ }
+ } else {
+ p = takeIPv4Address(p, n, "hex digits or IPv4 address");
+ ipv6byteCount += 4;
+ }
+ return p;
+ }
+
+ // Scan a hex sequence; return -1 if one could not be scanned
+ //
+ private int scanHexSeq(int start, int n)
+ throws URISyntaxException
+ {
+ int p = start;
+ int q;
+
+ q = scan(p, n, L_HEX, H_HEX);
+ if (q <= p)
+ return -1;
+ if (at(q, n, '.')) // Beginning of IPv4 address
+ return -1;
+ if (q > p + 4)
+ fail("IPv6 hexadecimal digit sequence too long", p);
+ ipv6byteCount += 2;
+ p = q;
+ while (p < n) {
+ if (!at(p, n, ':'))
+ break;
+ if (at(p + 1, n, ':'))
+ break; // "::"
+ p++;
+ q = scan(p, n, L_HEX, H_HEX);
+ if (q <= p)
+ failExpecting("digits for an IPv6 address", p);
+ if (at(q, n, '.')) { // Beginning of IPv4 address
+ p--;
+ break;
+ }
+ if (q > p + 4)
+ fail("IPv6 hexadecimal digit sequence too long", p);
+ ipv6byteCount += 2;
+ p = q;
+ }
+
+ return p;
+ }
+
+ }
+
+}
diff --git a/java/net/URISyntaxException.java b/java/net/URISyntaxException.java
new file mode 100644
index 0000000..8072c37
--- /dev/null
+++ b/java/net/URISyntaxException.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+
+/**
+ * Checked exception thrown to indicate that a string could not be parsed as a
+ * URI reference.
+ *
+ * @author Mark Reinhold
+ * @see URI
+ * @since 1.4
+ */
+
+public class URISyntaxException
+ extends Exception
+{
+ private static final long serialVersionUID = 2137979680897488891L;
+
+ private String input;
+ private int index;
+
+ /**
+ * Constructs an instance from the given input string, reason, and error
+ * index.
+ *
+ * @param input The input string
+ * @param reason A string explaining why the input could not be parsed
+ * @param index The index at which the parse error occurred,
+ * or {@code -1} if the index is not known
+ *
+ * @throws NullPointerException
+ * If either the input or reason strings are {@code null}
+ *
+ * @throws IllegalArgumentException
+ * If the error index is less than {@code -1}
+ */
+ public URISyntaxException(String input, String reason, int index) {
+ super(reason);
+ if ((input == null) || (reason == null))
+ throw new NullPointerException();
+ if (index < -1)
+ throw new IllegalArgumentException();
+ this.input = input;
+ this.index = index;
+ }
+
+ /**
+ * Constructs an instance from the given input string and reason. The
+ * resulting object will have an error index of {@code -1}.
+ *
+ * @param input The input string
+ * @param reason A string explaining why the input could not be parsed
+ *
+ * @throws NullPointerException
+ * If either the input or reason strings are {@code null}
+ */
+ public URISyntaxException(String input, String reason) {
+ this(input, reason, -1);
+ }
+
+ /**
+ * Returns the input string.
+ *
+ * @return The input string
+ */
+ public String getInput() {
+ return input;
+ }
+
+ /**
+ * Returns a string explaining why the input string could not be parsed.
+ *
+ * @return The reason string
+ */
+ public String getReason() {
+ return super.getMessage();
+ }
+
+ /**
+ * Returns an index into the input string of the position at which the
+ * parse error occurred, or {@code -1} if this position is not known.
+ *
+ * @return The error index
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * Returns a string describing the parse error. The resulting string
+ * consists of the reason string followed by a colon character
+ * ({@code ':'}), a space, and the input string. If the error index is
+ * defined then the string {@code " at index "} followed by the index, in
+ * decimal, is inserted after the reason string and before the colon
+ * character.
+ *
+ * @return A string describing the parse error
+ */
+ public String getMessage() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(getReason());
+ if (index > -1) {
+ sb.append(" at index ");
+ sb.append(index);
+ }
+ sb.append(": ");
+ sb.append(input);
+ return sb.toString();
+ }
+
+}
diff --git a/java/net/URL.java b/java/net/URL.java
new file mode 100644
index 0000000..74dec92
--- /dev/null
+++ b/java/net/URL.java
@@ -0,0 +1,1627 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream.GetField;
+import java.io.ObjectStreamException;
+import java.io.ObjectStreamField;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+import java.util.StringTokenizer;
+import sun.security.util.SecurityConstants;
+
+/**
+ * Class {@code URL} represents a Uniform Resource
+ * Locator, a pointer to a "resource" on the World
+ * Wide Web. A resource can be something as simple as a file or a
+ * directory, or it can be a reference to a more complicated object,
+ * such as a query to a database or to a search engine. More
+ * information on the types of URLs and their formats can be found at:
+ * <a href=
+ * "http://web.archive.org/web/20051219043731/http://archive.ncsa.uiuc.edu/SDG/Software/Mosaic/Demo/url-primer.html">
+ * <i>Types of URL</i></a>
+ * <p>
+ * In general, a URL can be broken into several parts. Consider the
+ * following example:
+ * <blockquote><pre>
+ * http://www.example.com/docs/resource1.html
+ * </pre></blockquote>
+ * <p>
+ * The URL above indicates that the protocol to use is
+ * {@code http} (HyperText Transfer Protocol) and that the
+ * information resides on a host machine named
+ * {@code www.example.com}. The information on that host
+ * machine is named {@code /docs/resource1.html}. The exact
+ * meaning of this name on the host machine is both protocol
+ * dependent and host dependent. The information normally resides in
+ * a file, but it could be generated on the fly. This component of
+ * the URL is called the <i>path</i> component.
+ * <p>
+ * A URL can optionally specify a "port", which is the
+ * port number to which the TCP connection is made on the remote host
+ * machine. If the port is not specified, the default port for
+ * the protocol is used instead. For example, the default port for
+ * {@code http} is {@code 80}. An alternative port could be
+ * specified as:
+ * <blockquote><pre>
+ * http://www.example.com:1080/docs/resource1.html
+ * </pre></blockquote>
+ * <p>
+ * The syntax of {@code URL} is defined by <a
+ * href="http://www.ietf.org/rfc/rfc2396.txt"><i>RFC 2396: Uniform
+ * Resource Identifiers (URI): Generic Syntax</i></a>, amended by <a
+ * href="http://www.ietf.org/rfc/rfc2732.txt"><i>RFC 2732: Format for
+ * Literal IPv6 Addresses in URLs</i></a>. The Literal IPv6 address format
+ * also supports scope_ids. The syntax and usage of scope_ids is described
+ * <a href="Inet6Address.html#scoped">here</a>.
+ * <p>
+ * A URL may have appended to it a "fragment", also known
+ * as a "ref" or a "reference". The fragment is indicated by the sharp
+ * sign character "#" followed by more characters. For example,
+ * <blockquote><pre>
+ * http://java.sun.com/index.html#chapter1
+ * </pre></blockquote>
+ * <p>
+ * This fragment is not technically part of the URL. Rather, it
+ * indicates that after the specified resource is retrieved, the
+ * application is specifically interested in that part of the
+ * document that has the tag {@code chapter1} attached to it. The
+ * meaning of a tag is resource specific.
+ * <p>
+ * An application can also specify a "relative URL",
+ * which contains only enough information to reach the resource
+ * relative to another URL. Relative URLs are frequently used within
+ * HTML pages. For example, if the contents of the URL:
+ * <blockquote><pre>
+ * http://java.sun.com/index.html
+ * </pre></blockquote>
+ * contained within it the relative URL:
+ * <blockquote><pre>
+ * FAQ.html
+ * </pre></blockquote>
+ * it would be a shorthand for:
+ * <blockquote><pre>
+ * http://java.sun.com/FAQ.html
+ * </pre></blockquote>
+ * <p>
+ * The relative URL need not specify all the components of a URL. If
+ * the protocol, host name, or port number is missing, the value is
+ * inherited from the fully specified URL. The file component must be
+ * specified. The optional fragment is not inherited.
+ * <p>
+ * The URL class does not itself encode or decode any URL components
+ * according to the escaping mechanism defined in RFC2396. It is the
+ * responsibility of the caller to encode any fields, which need to be
+ * escaped prior to calling URL, and also to decode any escaped fields,
+ * that are returned from URL. Furthermore, because URL has no knowledge
+ * of URL escaping, it does not recognise equivalence between the encoded
+ * or decoded form of the same URL. For example, the two URLs:<br>
+ * <pre> http://foo.com/hello world/ and http://foo.com/hello%20world</pre>
+ * would be considered not equal to each other.
+ * <p>
+ * Note, the {@link java.net.URI} class does perform escaping of its
+ * component fields in certain circumstances. The recommended way
+ * to manage the encoding and decoding of URLs is to use {@link java.net.URI},
+ * and to convert between these two classes using {@link #toURI()} and
+ * {@link URI#toURL()}.
+ * <p>
+ * The {@link URLEncoder} and {@link URLDecoder} classes can also be
+ * used, but only for HTML form encoding, which is not the same
+ * as the encoding scheme defined in RFC2396.
+ *
+ * @author James Gosling
+ * @since JDK1.0
+ */
+public final class URL implements java.io.Serializable {
+
+ // Android-changed: Custom built-in URLStreamHandlers for http, https.
+ // static final String BUILTIN_HANDLERS_PREFIX = "sun.net.www.protocol";
+ private static final Set<String> BUILTIN_HANDLER_CLASS_NAMES = createBuiltinHandlerClassNames();
+ static final long serialVersionUID = -7627629688361524110L;
+
+ /**
+ * The property which specifies the package prefix list to be scanned
+ * for protocol handlers. The value of this property (if any) should
+ * be a vertical bar delimited list of package names to search through
+ * for a protocol handler to load. The policy of this class is that
+ * all protocol handlers will be in a class called <protocolname>.Handler,
+ * and each package in the list is examined in turn for a matching
+ * handler. If none are found (or the property is not specified), the
+ * default package prefix, sun.net.www.protocol, is used. The search
+ * proceeds from the first package in the list to the last and stops
+ * when a match is found.
+ */
+ private static final String protocolPathProp = "java.protocol.handler.pkgs";
+
+ /**
+ * The protocol to use (ftp, http, nntp, ... etc.) .
+ * @serial
+ */
+ private String protocol;
+
+ /**
+ * The host name to connect to.
+ * @serial
+ */
+ private String host;
+
+ /**
+ * The protocol port to connect to.
+ * @serial
+ */
+ private int port = -1;
+
+ /**
+ * The specified file name on that host. {@code file} is
+ * defined as {@code path[?query]}
+ * @serial
+ */
+ private String file;
+
+ /**
+ * The query part of this URL.
+ */
+ private transient String query;
+
+ /**
+ * The authority part of this URL.
+ * @serial
+ */
+ private String authority;
+
+ /**
+ * The path part of this URL.
+ */
+ private transient String path;
+
+ /**
+ * The userinfo part of this URL.
+ */
+ private transient String userInfo;
+
+ /**
+ * # reference.
+ * @serial
+ */
+ private String ref;
+
+ /**
+ * The host's IP address, used in equals and hashCode.
+ * Computed on demand. An uninitialized or unknown hostAddress is null.
+ */
+ transient InetAddress hostAddress;
+
+ /**
+ * The URLStreamHandler for this URL.
+ */
+ transient URLStreamHandler handler;
+
+ /* Our hash code.
+ * @serial
+ */
+ private int hashCode = -1;
+
+ private transient UrlDeserializedState tempState;
+
+ /**
+ * Creates a {@code URL} object from the specified
+ * {@code protocol}, {@code host}, {@code port}
+ * number, and {@code file}.<p>
+ *
+ * {@code host} can be expressed as a host name or a literal
+ * IP address. If IPv6 literal address is used, it should be
+ * enclosed in square brackets ({@code '['} and {@code ']'}), as
+ * specified by <a
+ * href="http://www.ietf.org/rfc/rfc2732.txt">RFC 2732</a>;
+ * However, the literal IPv6 address format defined in <a
+ * href="http://www.ietf.org/rfc/rfc2373.txt"><i>RFC 2373: IP
+ * Version 6 Addressing Architecture</i></a> is also accepted.<p>
+ *
+ * Specifying a {@code port} number of {@code -1}
+ * indicates that the URL should use the default port for the
+ * protocol.<p>
+ *
+ * If this is the first URL object being created with the specified
+ * protocol, a <i>stream protocol handler</i> object, an instance of
+ * class {@code URLStreamHandler}, is created for that protocol:
+ * <ol>
+ * <li>If the application has previously set up an instance of
+ * {@code URLStreamHandlerFactory} as the stream handler factory,
+ * then the {@code createURLStreamHandler} method of that instance
+ * is called with the protocol string as an argument to create the
+ * stream protocol handler.
+ * <li>If no {@code URLStreamHandlerFactory} has yet been set up,
+ * or if the factory's {@code createURLStreamHandler} method
+ * returns {@code null}, then the constructor finds the
+ * value of the system property:
+ * <blockquote><pre>
+ * java.protocol.handler.pkgs
+ * </pre></blockquote>
+ * If the value of that system property is not {@code null},
+ * it is interpreted as a list of packages separated by a vertical
+ * slash character '{@code |}'. The constructor tries to load
+ * the class named:
+ * <blockquote><pre>
+ * <<i>package</i>>.<<i>protocol</i>>.Handler
+ * </pre></blockquote>
+ * where <<i>package</i>> is replaced by the name of the package
+ * and <<i>protocol</i>> is replaced by the name of the protocol.
+ * If this class does not exist, or if the class exists but it is not
+ * a subclass of {@code URLStreamHandler}, then the next package
+ * in the list is tried.
+ * <li>If the previous step fails to find a protocol handler, then the
+ * constructor tries to load from a system default package.
+ * <blockquote><pre>
+ * <<i>system default package</i>>.<<i>protocol</i>>.Handler
+ * </pre></blockquote>
+ * If this class does not exist, or if the class exists but it is not a
+ * subclass of {@code URLStreamHandler}, then a
+ * {@code MalformedURLException} is thrown.
+ * </ol>
+ *
+ * <p>Protocol handlers for the following protocols are guaranteed
+ * to exist on the search path :-
+ * <blockquote><pre>
+ * http, https, file, and jar
+ * </pre></blockquote>
+ * Protocol handlers for additional protocols may also be
+ * available.
+ *
+ * <p>No validation of the inputs is performed by this constructor.
+ *
+ * @param protocol the name of the protocol to use.
+ * @param host the name of the host.
+ * @param port the port number on the host.
+ * @param file the file on the host
+ * @exception MalformedURLException if an unknown protocol is specified.
+ * @see java.lang.System#getProperty(java.lang.String)
+ * @see java.net.URL#setURLStreamHandlerFactory(
+ * java.net.URLStreamHandlerFactory)
+ * @see java.net.URLStreamHandler
+ * @see java.net.URLStreamHandlerFactory#createURLStreamHandler(
+ * java.lang.String)
+ */
+ public URL(String protocol, String host, int port, String file)
+ throws MalformedURLException
+ {
+ this(protocol, host, port, file, null);
+ }
+
+ /**
+ * Creates a URL from the specified {@code protocol}
+ * name, {@code host} name, and {@code file} name. The
+ * default port for the specified protocol is used.
+ * <p>
+ * This method is equivalent to calling the four-argument
+ * constructor with the arguments being {@code protocol},
+ * {@code host}, {@code -1}, and {@code file}.
+ *
+ * No validation of the inputs is performed by this constructor.
+ *
+ * @param protocol the name of the protocol to use.
+ * @param host the name of the host.
+ * @param file the file on the host.
+ * @exception MalformedURLException if an unknown protocol is specified.
+ * @see java.net.URL#URL(java.lang.String, java.lang.String,
+ * int, java.lang.String)
+ */
+ public URL(String protocol, String host, String file)
+ throws MalformedURLException {
+ this(protocol, host, -1, file);
+ }
+
+ /**
+ * Creates a {@code URL} object from the specified
+ * {@code protocol}, {@code host}, {@code port}
+ * number, {@code file}, and {@code handler}. Specifying
+ * a {@code port} number of {@code -1} indicates that
+ * the URL should use the default port for the protocol. Specifying
+ * a {@code handler} of {@code null} indicates that the URL
+ * should use a default stream handler for the protocol, as outlined
+ * for:
+ * java.net.URL#URL(java.lang.String, java.lang.String, int,
+ * java.lang.String)
+ *
+ * <p>If the handler is not null and there is a security manager,
+ * the security manager's {@code checkPermission}
+ * method is called with a
+ * {@code NetPermission("specifyStreamHandler")} permission.
+ * This may result in a SecurityException.
+ *
+ * No validation of the inputs is performed by this constructor.
+ *
+ * @param protocol the name of the protocol to use.
+ * @param host the name of the host.
+ * @param port the port number on the host.
+ * @param file the file on the host
+ * @param handler the stream handler for the URL.
+ * @exception MalformedURLException if an unknown protocol is specified.
+ * @exception SecurityException
+ * if a security manager exists and its
+ * {@code checkPermission} method doesn't allow
+ * specifying a stream handler explicitly.
+ * @see java.lang.System#getProperty(java.lang.String)
+ * @see java.net.URL#setURLStreamHandlerFactory(
+ * java.net.URLStreamHandlerFactory)
+ * @see java.net.URLStreamHandler
+ * @see java.net.URLStreamHandlerFactory#createURLStreamHandler(
+ * java.lang.String)
+ * @see SecurityManager#checkPermission
+ * @see java.net.NetPermission
+ */
+ public URL(String protocol, String host, int port, String file,
+ URLStreamHandler handler) throws MalformedURLException {
+ if (handler != null) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ // check for permission to specify a handler
+ checkSpecifyHandler(sm);
+ }
+ }
+
+ protocol = protocol.toLowerCase();
+ this.protocol = protocol;
+ if (host != null) {
+
+ /**
+ * if host is a literal IPv6 address,
+ * we will make it conform to RFC 2732
+ */
+ if (host.indexOf(':') >= 0 && !host.startsWith("[")) {
+ host = "["+host+"]";
+ }
+ this.host = host;
+
+ if (port < -1) {
+ throw new MalformedURLException("Invalid port number :" +
+ port);
+ }
+ this.port = port;
+ authority = (port == -1) ? host : host + ":" + port;
+ }
+
+ // Android-changed: App compat. Prepend '/' if host is null / empty
+ // Parts parts = new Parts(file);
+ Parts parts = new Parts(file, host);
+ path = parts.getPath();
+ query = parts.getQuery();
+
+ if (query != null) {
+ this.file = path + "?" + query;
+ } else {
+ this.file = path;
+ }
+ ref = parts.getRef();
+
+ // Note: we don't do validation of the URL here. Too risky to change
+ // right now, but worth considering for future reference. -br
+ if (handler == null &&
+ (handler = getURLStreamHandler(protocol)) == null) {
+ throw new MalformedURLException("unknown protocol: " + protocol);
+ }
+ this.handler = handler;
+ }
+
+ /**
+ * Creates a {@code URL} object from the {@code String}
+ * representation.
+ * <p>
+ * This constructor is equivalent to a call to the two-argument
+ * constructor with a {@code null} first argument.
+ *
+ * @param spec the {@code String} to parse as a URL.
+ * @exception MalformedURLException if no protocol is specified, or an
+ * unknown protocol is found, or {@code spec} is {@code null}.
+ * @see java.net.URL#URL(java.net.URL, java.lang.String)
+ */
+ public URL(String spec) throws MalformedURLException {
+ this(null, spec);
+ }
+
+ /**
+ * Creates a URL by parsing the given spec within a specified context.
+ *
+ * The new URL is created from the given context URL and the spec
+ * argument as described in
+ * RFC2396 "Uniform Resource Identifiers : Generic * Syntax" :
+ * <blockquote><pre>
+ * <scheme>://<authority><path>?<query>#<fragment>
+ * </pre></blockquote>
+ * The reference is parsed into the scheme, authority, path, query and
+ * fragment parts. If the path component is empty and the scheme,
+ * authority, and query components are undefined, then the new URL is a
+ * reference to the current document. Otherwise, the fragment and query
+ * parts present in the spec are used in the new URL.
+ * <p>
+ * If the scheme component is defined in the given spec and does not match
+ * the scheme of the context, then the new URL is created as an absolute
+ * URL based on the spec alone. Otherwise the scheme component is inherited
+ * from the context URL.
+ * <p>
+ * If the authority component is present in the spec then the spec is
+ * treated as absolute and the spec authority and path will replace the
+ * context authority and path. If the authority component is absent in the
+ * spec then the authority of the new URL will be inherited from the
+ * context.
+ * <p>
+ * If the spec's path component begins with a slash character
+ * "/" then the
+ * path is treated as absolute and the spec path replaces the context path.
+ * <p>
+ * Otherwise, the path is treated as a relative path and is appended to the
+ * context path, as described in RFC2396. Also, in this case,
+ * the path is canonicalized through the removal of directory
+ * changes made by occurrences of ".." and ".".
+ * <p>
+ * For a more detailed description of URL parsing, refer to RFC2396.
+ *
+ * @param context the context in which to parse the specification.
+ * @param spec the {@code String} to parse as a URL.
+ * @exception MalformedURLException if no protocol is specified, or an
+ * unknown protocol is found, or {@code spec} is {@code null}.
+ * @see java.net.URL#URL(java.lang.String, java.lang.String,
+ * int, java.lang.String)
+ * @see java.net.URLStreamHandler
+ * @see java.net.URLStreamHandler#parseURL(java.net.URL,
+ * java.lang.String, int, int)
+ */
+ public URL(URL context, String spec) throws MalformedURLException {
+ this(context, spec, null);
+ }
+
+ /**
+ * Creates a URL by parsing the given spec with the specified handler
+ * within a specified context. If the handler is null, the parsing
+ * occurs as with the two argument constructor.
+ *
+ * @param context the context in which to parse the specification.
+ * @param spec the {@code String} to parse as a URL.
+ * @param handler the stream handler for the URL.
+ * @exception MalformedURLException if no protocol is specified, or an
+ * unknown protocol is found, or {@code spec} is {@code null}.
+ * @exception SecurityException
+ * if a security manager exists and its
+ * {@code checkPermission} method doesn't allow
+ * specifying a stream handler.
+ * @see java.net.URL#URL(java.lang.String, java.lang.String,
+ * int, java.lang.String)
+ * @see java.net.URLStreamHandler
+ * @see java.net.URLStreamHandler#parseURL(java.net.URL,
+ * java.lang.String, int, int)
+ */
+ public URL(URL context, String spec, URLStreamHandler handler)
+ throws MalformedURLException
+ {
+ String original = spec;
+ int i, limit, c;
+ int start = 0;
+ String newProtocol = null;
+ boolean aRef=false;
+ boolean isRelative = false;
+
+ // Check for permission to specify a handler
+ if (handler != null) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ checkSpecifyHandler(sm);
+ }
+ }
+
+ try {
+ limit = spec.length();
+ while ((limit > 0) && (spec.charAt(limit - 1) <= ' ')) {
+ limit--; //eliminate trailing whitespace
+ }
+ while ((start < limit) && (spec.charAt(start) <= ' ')) {
+ start++; // eliminate leading whitespace
+ }
+
+ if (spec.regionMatches(true, start, "url:", 0, 4)) {
+ start += 4;
+ }
+ if (start < spec.length() && spec.charAt(start) == '#') {
+ /* we're assuming this is a ref relative to the context URL.
+ * This means protocols cannot start w/ '#', but we must parse
+ * ref URL's like: "hello:there" w/ a ':' in them.
+ */
+ aRef=true;
+ }
+ for (i = start ; !aRef && (i < limit) &&
+ ((c = spec.charAt(i)) != '/') ; i++) {
+ if (c == ':') {
+
+ String s = spec.substring(start, i).toLowerCase();
+ if (isValidProtocol(s)) {
+ newProtocol = s;
+ start = i + 1;
+ }
+ break;
+ }
+ }
+
+ // Only use our context if the protocols match.
+ protocol = newProtocol;
+ if ((context != null) && ((newProtocol == null) ||
+ newProtocol.equalsIgnoreCase(context.protocol))) {
+ // inherit the protocol handler from the context
+ // if not specified to the constructor
+ if (handler == null) {
+ handler = context.handler;
+ }
+
+ // If the context is a hierarchical URL scheme and the spec
+ // contains a matching scheme then maintain backwards
+ // compatibility and treat it as if the spec didn't contain
+ // the scheme; see 5.2.3 of RFC2396
+ if (context.path != null && context.path.startsWith("/"))
+ newProtocol = null;
+
+ if (newProtocol == null) {
+ protocol = context.protocol;
+ authority = context.authority;
+ userInfo = context.userInfo;
+ host = context.host;
+ port = context.port;
+ file = context.file;
+ path = context.path;
+ isRelative = true;
+ }
+ }
+
+ if (protocol == null) {
+ throw new MalformedURLException("no protocol: "+original);
+ }
+
+ // Get the protocol handler if not specified or the protocol
+ // of the context could not be used
+ if (handler == null &&
+ (handler = getURLStreamHandler(protocol)) == null) {
+ throw new MalformedURLException("unknown protocol: "+protocol);
+ }
+
+ this.handler = handler;
+
+ i = spec.indexOf('#', start);
+ if (i >= 0) {
+ ref = spec.substring(i + 1, limit);
+ limit = i;
+ }
+
+ /*
+ * Handle special case inheritance of query and fragment
+ * implied by RFC2396 section 5.2.2.
+ */
+ if (isRelative && start == limit) {
+ query = context.query;
+ if (ref == null) {
+ ref = context.ref;
+ }
+ }
+
+ handler.parseURL(this, spec, start, limit);
+
+ } catch(MalformedURLException e) {
+ throw e;
+ } catch(Exception e) {
+ MalformedURLException exception = new MalformedURLException(e.getMessage());
+ exception.initCause(e);
+ throw exception;
+ }
+ }
+
+ /*
+ * Returns true if specified string is a valid protocol name.
+ */
+ private boolean isValidProtocol(String protocol) {
+ int len = protocol.length();
+ if (len < 1)
+ return false;
+ char c = protocol.charAt(0);
+ if (!Character.isLetter(c))
+ return false;
+ for (int i = 1; i < len; i++) {
+ c = protocol.charAt(i);
+ if (!Character.isLetterOrDigit(c) && c != '.' && c != '+' &&
+ c != '-') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /*
+ * Checks for permission to specify a stream handler.
+ */
+ private void checkSpecifyHandler(SecurityManager sm) {
+ sm.checkPermission(SecurityConstants.SPECIFY_HANDLER_PERMISSION);
+ }
+
+ /**
+ * Sets the fields of the URL. This is not a public method so that
+ * only URLStreamHandlers can modify URL fields. URLs are
+ * otherwise constant.
+ *
+ * @param protocol the name of the protocol to use
+ * @param host the name of the host
+ @param port the port number on the host
+ * @param file the file on the host
+ * @param ref the internal reference in the URL
+ */
+ void set(String protocol, String host, int port,
+ String file, String ref) {
+ synchronized (this) {
+ this.protocol = protocol;
+ this.host = host;
+ authority = port == -1 ? host : host + ":" + port;
+ this.port = port;
+ this.file = file;
+ this.ref = ref;
+ /* This is very important. We must recompute this after the
+ * URL has been changed. */
+ hashCode = -1;
+ hostAddress = null;
+ int q = file.lastIndexOf('?');
+ if (q != -1) {
+ query = file.substring(q+1);
+ path = file.substring(0, q);
+ } else
+ path = file;
+ }
+ }
+
+ /**
+ * Sets the specified 8 fields of the URL. This is not a public method so
+ * that only URLStreamHandlers can modify URL fields. URLs are otherwise
+ * constant.
+ *
+ * @param protocol the name of the protocol to use
+ * @param host the name of the host
+ * @param port the port number on the host
+ * @param authority the authority part for the url
+ * @param userInfo the username and password
+ * @param path the file on the host
+ * @param ref the internal reference in the URL
+ * @param query the query part of this URL
+ * @since 1.3
+ */
+ void set(String protocol, String host, int port,
+ String authority, String userInfo, String path,
+ String query, String ref) {
+ synchronized (this) {
+ this.protocol = protocol;
+ this.host = host;
+ this.port = port;
+ // Android-changed: App compat. Only include query part if it's nonempty.
+ // this.file = query == null ? path : path + "?" + query;
+ this.file = (query == null || query.isEmpty()) ? path : path + "?" + query;
+ this.userInfo = userInfo;
+ this.path = path;
+ this.ref = ref;
+ /* This is very important. We must recompute this after the
+ * URL has been changed. */
+ hashCode = -1;
+ hostAddress = null;
+ this.query = query;
+ this.authority = authority;
+ }
+ }
+
+ /**
+ * Gets the query part of this {@code URL}.
+ *
+ * @return the query part of this {@code URL},
+ * or <CODE>null</CODE> if one does not exist
+ * @since 1.3
+ */
+ public String getQuery() {
+ return query;
+ }
+
+ /**
+ * Gets the path part of this {@code URL}.
+ *
+ * @return the path part of this {@code URL}, or an
+ * empty string if one does not exist
+ * @since 1.3
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * Gets the userInfo part of this {@code URL}.
+ *
+ * @return the userInfo part of this {@code URL}, or
+ * <CODE>null</CODE> if one does not exist
+ * @since 1.3
+ */
+ public String getUserInfo() {
+ return userInfo;
+ }
+
+ /**
+ * Gets the authority part of this {@code URL}.
+ *
+ * @return the authority part of this {@code URL}
+ * @since 1.3
+ */
+ public String getAuthority() {
+ return authority;
+ }
+
+ /**
+ * Gets the port number of this {@code URL}.
+ *
+ * @return the port number, or -1 if the port is not set
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * Gets the default port number of the protocol associated
+ * with this {@code URL}. If the URL scheme or the URLStreamHandler
+ * for the URL do not define a default port number,
+ * then -1 is returned.
+ *
+ * @return the port number
+ * @since 1.4
+ */
+ public int getDefaultPort() {
+ return handler.getDefaultPort();
+ }
+
+ /**
+ * Gets the protocol name of this {@code URL}.
+ *
+ * @return the protocol of this {@code URL}.
+ */
+ public String getProtocol() {
+ return protocol;
+ }
+
+ /**
+ * Gets the host name of this {@code URL}, if applicable.
+ * The format of the host conforms to RFC 2732, i.e. for a
+ * literal IPv6 address, this method will return the IPv6 address
+ * enclosed in square brackets ({@code '['} and {@code ']'}).
+ *
+ * @return the host name of this {@code URL}.
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * Gets the file name of this {@code URL}.
+ * The returned file portion will be
+ * the same as <CODE>getPath()</CODE>, plus the concatenation of
+ * the value of <CODE>getQuery()</CODE>, if any. If there is
+ * no query portion, this method and <CODE>getPath()</CODE> will
+ * return identical results.
+ *
+ * @return the file name of this {@code URL},
+ * or an empty string if one does not exist
+ */
+ public String getFile() {
+ return file;
+ }
+
+ /**
+ * Gets the anchor (also known as the "reference") of this
+ * {@code URL}.
+ *
+ * @return the anchor (also known as the "reference") of this
+ * {@code URL}, or <CODE>null</CODE> if one does not exist
+ */
+ public String getRef() {
+ return ref;
+ }
+
+ // Android-changed: Don't let URL.equals() attempt to resolve host names.
+ /**
+ * Compares this URL for equality with another object.<p>
+ *
+ * If the given object is not a URL then this method immediately returns
+ * {@code false}.<p>
+ *
+ * Two URL objects are equal if they have the same protocol, reference
+ * equivalent hosts, have the same port number on the host, and the same
+ * file and fragment of the file.<p>
+ *
+ * Returns true if this URL equals {@code o}. URLs are equal if they have
+ * the same protocol, host, port, file, and reference.
+ *
+ * <h3>Network I/O Warning</h3>
+ * <p>Some implementations of URL.equals() resolve host names over the
+ * network. This is problematic:
+ * <ul>
+ * <li><strong>The network may be slow.</strong> Many classes, including
+ * core collections like {@link java.util.Map Map} and {@link java.util.Set
+ * Set} expect that {@code equals} and {@code hashCode} will return quickly.
+ * By violating this assumption, this method posed potential performance
+ * problems.
+ * <li><strong>Equal IP addresses do not imply equal content.</strong>
+ * Virtual hosting permits unrelated sites to share an IP address. This
+ * method could report two otherwise unrelated URLs to be equal because
+ * they're hosted on the same server.</li>
+ * <li><strong>The network may not be available.</strong> Two URLs could be
+ * equal when a network is available and unequal otherwise.</li>
+ * <li><strong>The network may change.</strong> The IP address for a given
+ * host name varies by network and over time. This is problematic for mobile
+ * devices. Two URLs could be equal on some networks and unequal on
+ * others.</li>
+ * </ul>
+ * <p>This problem is fixed in Android 4.0 (Ice Cream Sandwich). In that
+ * release, URLs are only equal if their host names are equal (ignoring
+ * case).
+ *
+ * @param obj the URL to compare against.
+ * @return {@code true} if the objects are the same;
+ * {@code false} otherwise.
+ */
+ public boolean equals(Object obj) {
+ if (!(obj instanceof URL))
+ return false;
+ URL u2 = (URL)obj;
+
+ return handler.equals(this, u2);
+ }
+
+ /**
+ * Creates an integer suitable for hash table indexing.<p>
+ *
+ * The hash code is based upon all the URL components relevant for URL
+ * comparison. As such, this operation is a blocking operation.<p>
+ *
+ * @return a hash code for this {@code URL}.
+ */
+ public synchronized int hashCode() {
+ if (hashCode != -1)
+ return hashCode;
+
+ hashCode = handler.hashCode(this);
+ return hashCode;
+ }
+
+ /**
+ * Compares two URLs, excluding the fragment component.<p>
+ *
+ * Returns {@code true} if this {@code URL} and the
+ * {@code other} argument are equal without taking the
+ * fragment component into consideration.
+ *
+ * @param other the {@code URL} to compare against.
+ * @return {@code true} if they reference the same remote object;
+ * {@code false} otherwise.
+ */
+ public boolean sameFile(URL other) {
+ return handler.sameFile(this, other);
+ }
+
+ /**
+ * Constructs a string representation of this {@code URL}. The
+ * string is created by calling the {@code toExternalForm}
+ * method of the stream protocol handler for this object.
+ *
+ * @return a string representation of this object.
+ * @see java.net.URL#URL(java.lang.String, java.lang.String, int,
+ * java.lang.String)
+ * @see java.net.URLStreamHandler#toExternalForm(java.net.URL)
+ */
+ public String toString() {
+ return toExternalForm();
+ }
+
+ /**
+ * Constructs a string representation of this {@code URL}. The
+ * string is created by calling the {@code toExternalForm}
+ * method of the stream protocol handler for this object.
+ *
+ * @return a string representation of this object.
+ * @see java.net.URL#URL(java.lang.String, java.lang.String,
+ * int, java.lang.String)
+ * @see java.net.URLStreamHandler#toExternalForm(java.net.URL)
+ */
+ public String toExternalForm() {
+ return handler.toExternalForm(this);
+ }
+
+ /**
+ * Returns a {@link java.net.URI} equivalent to this URL.
+ * This method functions in the same way as {@code new URI (this.toString())}.
+ * <p>Note, any URL instance that complies with RFC 2396 can be converted
+ * to a URI. However, some URLs that are not strictly in compliance
+ * can not be converted to a URI.
+ *
+ * @exception URISyntaxException if this URL is not formatted strictly according to
+ * to RFC2396 and cannot be converted to a URI.
+ *
+ * @return a URI instance equivalent to this URL.
+ * @since 1.5
+ */
+ public URI toURI() throws URISyntaxException {
+ return new URI (toString());
+ }
+
+ /**
+ * Returns a {@link java.net.URLConnection URLConnection} instance that
+ * represents a connection to the remote object referred to by the
+ * {@code URL}.
+ *
+ * <P>A new instance of {@linkplain java.net.URLConnection URLConnection} is
+ * created every time when invoking the
+ * {@linkplain java.net.URLStreamHandler#openConnection(URL)
+ * URLStreamHandler.openConnection(URL)} method of the protocol handler for
+ * this URL.</P>
+ *
+ * <P>It should be noted that a URLConnection instance does not establish
+ * the actual network connection on creation. This will happen only when
+ * calling {@linkplain java.net.URLConnection#connect() URLConnection.connect()}.</P>
+ *
+ * <P>If for the URL's protocol (such as HTTP or JAR), there
+ * exists a public, specialized URLConnection subclass belonging
+ * to one of the following packages or one of their subpackages:
+ * java.lang, java.io, java.util, java.net, the connection
+ * returned will be of that subclass. For example, for HTTP an
+ * HttpURLConnection will be returned, and for JAR a
+ * JarURLConnection will be returned.</P>
+ *
+ * @return a {@link java.net.URLConnection URLConnection} linking
+ * to the URL.
+ * @exception IOException if an I/O exception occurs.
+ * @see java.net.URL#URL(java.lang.String, java.lang.String,
+ * int, java.lang.String)
+ */
+ public URLConnection openConnection() throws java.io.IOException {
+ return handler.openConnection(this);
+ }
+
+ /**
+ * Same as {@link #openConnection()}, except that the connection will be
+ * made through the specified proxy; Protocol handlers that do not
+ * support proxing will ignore the proxy parameter and make a
+ * normal connection.
+ *
+ * Invoking this method preempts the system's default ProxySelector
+ * settings.
+ *
+ * @param proxy the Proxy through which this connection
+ * will be made. If direct connection is desired,
+ * Proxy.NO_PROXY should be specified.
+ * @return a {@code URLConnection} to the URL.
+ * @exception IOException if an I/O exception occurs.
+ * @exception SecurityException if a security manager is present
+ * and the caller doesn't have permission to connect
+ * to the proxy.
+ * @exception IllegalArgumentException will be thrown if proxy is null,
+ * or proxy has the wrong type
+ * @exception UnsupportedOperationException if the subclass that
+ * implements the protocol handler doesn't support
+ * this method.
+ * @see java.net.URL#URL(java.lang.String, java.lang.String,
+ * int, java.lang.String)
+ * @see java.net.URLConnection
+ * @see java.net.URLStreamHandler#openConnection(java.net.URL,
+ * java.net.Proxy)
+ * @since 1.5
+ */
+ public URLConnection openConnection(Proxy proxy)
+ throws java.io.IOException {
+ if (proxy == null) {
+ throw new IllegalArgumentException("proxy can not be null");
+ }
+
+ // Create a copy of Proxy as a security measure
+ Proxy p = proxy == Proxy.NO_PROXY ? Proxy.NO_PROXY : sun.net.ApplicationProxy.create(proxy);
+ SecurityManager sm = System.getSecurityManager();
+ if (p.type() != Proxy.Type.DIRECT && sm != null) {
+ InetSocketAddress epoint = (InetSocketAddress) p.address();
+ if (epoint.isUnresolved())
+ sm.checkConnect(epoint.getHostName(), epoint.getPort());
+ else
+ sm.checkConnect(epoint.getAddress().getHostAddress(),
+ epoint.getPort());
+ }
+ return handler.openConnection(this, p);
+ }
+
+ /**
+ * Opens a connection to this {@code URL} and returns an
+ * {@code InputStream} for reading from that connection. This
+ * method is a shorthand for:
+ * <blockquote><pre>
+ * openConnection().getInputStream()
+ * </pre></blockquote>
+ *
+ * @return an input stream for reading from the URL connection.
+ * @exception IOException if an I/O exception occurs.
+ * @see java.net.URL#openConnection()
+ * @see java.net.URLConnection#getInputStream()
+ */
+ public final InputStream openStream() throws java.io.IOException {
+ return openConnection().getInputStream();
+ }
+
+ /**
+ * Gets the contents of this URL. This method is a shorthand for:
+ * <blockquote><pre>
+ * openConnection().getContent()
+ * </pre></blockquote>
+ *
+ * @return the contents of this URL.
+ * @exception IOException if an I/O exception occurs.
+ * @see java.net.URLConnection#getContent()
+ */
+ public final Object getContent() throws java.io.IOException {
+ return openConnection().getContent();
+ }
+
+ /**
+ * Gets the contents of this URL. This method is a shorthand for:
+ * <blockquote><pre>
+ * openConnection().getContent(Class[])
+ * </pre></blockquote>
+ *
+ * @param classes an array of Java types
+ * @return the content object of this URL that is the first match of
+ * the types specified in the classes array.
+ * null if none of the requested types are supported.
+ * @exception IOException if an I/O exception occurs.
+ * @see java.net.URLConnection#getContent(Class[])
+ * @since 1.3
+ */
+ public final Object getContent(Class[] classes)
+ throws java.io.IOException {
+ return openConnection().getContent(classes);
+ }
+
+ /**
+ * The URLStreamHandler factory.
+ */
+ static URLStreamHandlerFactory factory;
+
+ /**
+ * Sets an application's {@code URLStreamHandlerFactory}.
+ * This method can be called at most once in a given Java Virtual
+ * Machine.
+ *
+ *<p> The {@code URLStreamHandlerFactory} instance is used to
+ *construct a stream protocol handler from a protocol name.
+ *
+ * <p> If there is a security manager, this method first calls
+ * the security manager's {@code checkSetFactory} method
+ * to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ *
+ * @param fac the desired factory.
+ * @exception Error if the application has already set a factory.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkSetFactory} method doesn't allow
+ * the operation.
+ * @see java.net.URL#URL(java.lang.String, java.lang.String,
+ * int, java.lang.String)
+ * @see java.net.URLStreamHandlerFactory
+ * @see SecurityManager#checkSetFactory
+ */
+ public static void setURLStreamHandlerFactory(URLStreamHandlerFactory fac) {
+ synchronized (streamHandlerLock) {
+ if (factory != null) {
+ throw new Error("factory already defined");
+ }
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkSetFactory();
+ }
+ handlers.clear();
+ factory = fac;
+ }
+ }
+
+ /**
+ * A table of protocol handlers.
+ */
+ static Hashtable<String,URLStreamHandler> handlers = new Hashtable<>();
+ private static Object streamHandlerLock = new Object();
+
+ /**
+ * Returns the Stream Handler.
+ * @param protocol the protocol to use
+ */
+ static URLStreamHandler getURLStreamHandler(String protocol) {
+
+ URLStreamHandler handler = handlers.get(protocol);
+ if (handler == null) {
+
+ boolean checkedWithFactory = false;
+
+ // Use the factory (if any)
+ if (factory != null) {
+ handler = factory.createURLStreamHandler(protocol);
+ checkedWithFactory = true;
+ }
+
+ // Try java protocol handler
+ if (handler == null) {
+ // Android-changed: Android doesn't need AccessController.
+ // Remove unnecessary use of reflection for sun classes
+ /*
+ packagePrefixList
+ = java.security.AccessController.doPrivileged(
+ new sun.security.action.GetPropertyAction(
+ protocolPathProp,""));
+ if (packagePrefixList != "") {
+ packagePrefixList += "|";
+ }
+
+ // REMIND: decide whether to allow the "null" class prefix
+ // or not.
+ packagePrefixList += "sun.net.www.protocol";
+ */
+ final String packagePrefixList = System.getProperty(protocolPathProp,"");
+
+ StringTokenizer packagePrefixIter =
+ new StringTokenizer(packagePrefixList, "|");
+
+ while (handler == null &&
+ packagePrefixIter.hasMoreTokens()) {
+
+ String packagePrefix =
+ packagePrefixIter.nextToken().trim();
+ try {
+ String clsName = packagePrefix + "." + protocol +
+ ".Handler";
+ Class<?> cls = null;
+ try {
+ ClassLoader cl = ClassLoader.getSystemClassLoader();
+ // BEGIN Android-changed: Fall back to thread's contextClassLoader.
+ // http://b/25897689
+ cls = Class.forName(clsName, true, cl);
+ } catch (ClassNotFoundException e) {
+ ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+ if (contextLoader != null) {
+ cls = Class.forName(clsName, true, contextLoader);
+ }
+ // END Android-changed: Fall back to thread's contextClassLoader.
+ }
+ if (cls != null) {
+ handler =
+ (URLStreamHandler)cls.newInstance();
+ }
+ } catch (ReflectiveOperationException ignored) {
+ }
+ }
+ }
+
+ // BEGIN Android-added: Custom built-in URLStreamHandlers for http, https.
+ // Fallback to built-in stream handler.
+ if (handler == null) {
+ try {
+ handler = createBuiltinHandler(protocol);
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+ // END Android-added: Custom built-in URLStreamHandlers for http, https.
+
+ synchronized (streamHandlerLock) {
+
+ URLStreamHandler handler2 = null;
+
+ // Check again with hashtable just in case another
+ // thread created a handler since we last checked
+ handler2 = handlers.get(protocol);
+
+ if (handler2 != null) {
+ return handler2;
+ }
+
+ // Check with factory if another thread set a
+ // factory since our last check
+ if (!checkedWithFactory && factory != null) {
+ handler2 = factory.createURLStreamHandler(protocol);
+ }
+
+ if (handler2 != null) {
+ // The handler from the factory must be given more
+ // importance. Discard the default handler that
+ // this thread created.
+ handler = handler2;
+ }
+
+ // Insert this handler into the hashtable
+ if (handler != null) {
+ handlers.put(protocol, handler);
+ }
+
+ }
+ }
+
+ return handler;
+
+ }
+
+ // BEGIN Android-added: Custom built-in URLStreamHandlers for http, https.
+ /**
+ * Returns an instance of the built-in handler for the given protocol, or null if none exists.
+ */
+ private static URLStreamHandler createBuiltinHandler(String protocol)
+ throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+ URLStreamHandler handler = null;
+ if (protocol.equals("file")) {
+ handler = new sun.net.www.protocol.file.Handler();
+ } else if (protocol.equals("ftp")) {
+ handler = new sun.net.www.protocol.ftp.Handler();
+ } else if (protocol.equals("jar")) {
+ handler = new sun.net.www.protocol.jar.Handler();
+ } else if (protocol.equals("http")) {
+ handler = (URLStreamHandler)Class.
+ forName("com.android.okhttp.HttpHandler").newInstance();
+ } else if (protocol.equals("https")) {
+ handler = (URLStreamHandler)Class.
+ forName("com.android.okhttp.HttpsHandler").newInstance();
+ }
+ return handler;
+ }
+
+ /** Names of implementation classes returned by {@link #createBuiltinHandler(String)}. */
+ private static Set<String> createBuiltinHandlerClassNames() {
+ Set<String> result = new HashSet<>();
+ // Refer to class names rather than classes to avoid needlessly triggering <clinit>.
+ result.add("sun.net.www.protocol.file.Handler");
+ result.add("sun.net.www.protocol.ftp.Handler");
+ result.add("sun.net.www.protocol.jar.Handler");
+ result.add("com.android.okhttp.HttpHandler");
+ result.add("com.android.okhttp.HttpsHandler");
+ return Collections.unmodifiableSet(result);
+ }
+ // END Android-added: Custom built-in URLStreamHandlers for http, https.
+
+ /**
+ * @serialField protocol String
+ *
+ * @serialField host String
+ *
+ * @serialField port int
+ *
+ * @serialField authority String
+ *
+ * @serialField file String
+ *
+ * @serialField ref String
+ *
+ * @serialField hashCode int
+ *
+ */
+ private static final ObjectStreamField[] serialPersistentFields = {
+ new ObjectStreamField("protocol", String.class),
+ new ObjectStreamField("host", String.class),
+ new ObjectStreamField("port", int.class),
+ new ObjectStreamField("authority", String.class),
+ new ObjectStreamField("file", String.class),
+ new ObjectStreamField("ref", String.class),
+ // Android-changed: App compat: hashCode should not be serialized.
+ // new ObjectStreamField("hashCode", int.class), };
+ };
+
+ /**
+ * WriteObject is called to save the state of the URL to an
+ * ObjectOutputStream. The handler is not saved since it is
+ * specific to this system.
+ *
+ * @serialData the default write object value. When read back in,
+ * the reader must ensure that calling getURLStreamHandler with
+ * the protocol variable returns a valid URLStreamHandler and
+ * throw an IOException if it does not.
+ */
+ private synchronized void writeObject(java.io.ObjectOutputStream s)
+ throws IOException
+ {
+ s.defaultWriteObject(); // write the fields
+ }
+
+ /**
+ * readObject is called to restore the state of the URL from the
+ * stream. It reads the components of the URL and finds the local
+ * stream handler.
+ */
+ private synchronized void readObject(java.io.ObjectInputStream s)
+ throws IOException, ClassNotFoundException {
+ GetField gf = s.readFields();
+ String protocol = (String)gf.get("protocol", null);
+ if (getURLStreamHandler(protocol) == null) {
+ throw new IOException("unknown protocol: " + protocol);
+ }
+ String host = (String)gf.get("host", null);
+ int port = gf.get("port", -1);
+ String authority = (String)gf.get("authority", null);
+ String file = (String)gf.get("file", null);
+ String ref = (String)gf.get("ref", null);
+ // Android-changed: App compat: hashCode should not be serialized.
+ // int hashCode = gf.get("hashCode", -1);
+ final int hashCode = -1;
+ if (authority == null
+ && ((host != null && host.length() > 0) || port != -1)) {
+ if (host == null)
+ host = "";
+ authority = (port == -1) ? host : host + ":" + port;
+ }
+ tempState = new UrlDeserializedState(protocol, host, port, authority,
+ file, ref, hashCode);
+ }
+
+ /**
+ * Replaces the de-serialized object with an URL object.
+ *
+ * @return a newly created object from the deserialzed state.
+ *
+ * @throws ObjectStreamException if a new object replacing this
+ * object could not be created
+ */
+
+ private Object readResolve() throws ObjectStreamException {
+
+ URLStreamHandler handler = null;
+ // already been checked in readObject
+ handler = getURLStreamHandler(tempState.getProtocol());
+
+ URL replacementURL = null;
+ if (isBuiltinStreamHandler(handler.getClass().getName())) {
+ replacementURL = fabricateNewURL();
+ } else {
+ replacementURL = setDeserializedFields(handler);
+ }
+ return replacementURL;
+ }
+
+ private URL setDeserializedFields(URLStreamHandler handler) {
+ URL replacementURL;
+ String userInfo = null;
+ String protocol = tempState.getProtocol();
+ String host = tempState.getHost();
+ int port = tempState.getPort();
+ String authority = tempState.getAuthority();
+ String file = tempState.getFile();
+ String ref = tempState.getRef();
+ int hashCode = tempState.getHashCode();
+
+
+ // Construct authority part
+ if (authority == null
+ && ((host != null && host.length() > 0) || port != -1)) {
+ if (host == null)
+ host = "";
+ authority = (port == -1) ? host : host + ":" + port;
+
+ // Handle hosts with userInfo in them
+ int at = host.lastIndexOf('@');
+ if (at != -1) {
+ userInfo = host.substring(0, at);
+ host = host.substring(at+1);
+ }
+ } else if (authority != null) {
+ // Construct user info part
+ int ind = authority.indexOf('@');
+ if (ind != -1)
+ userInfo = authority.substring(0, ind);
+ }
+
+ // Construct path and query part
+ String path = null;
+ String query = null;
+ if (file != null) {
+ // Fix: only do this if hierarchical?
+ int q = file.lastIndexOf('?');
+ if (q != -1) {
+ query = file.substring(q+1);
+ path = file.substring(0, q);
+ } else
+ path = file;
+ }
+
+ // Set the object fields.
+ this.protocol = protocol;
+ this.host = host;
+ this.port = port;
+ this.file = file;
+ this.authority = authority;
+ this.ref = ref;
+ this.hashCode = hashCode;
+ this.handler = handler;
+ this.query = query;
+ this.path = path;
+ this.userInfo = userInfo;
+ replacementURL = this;
+ return replacementURL;
+ }
+
+ private URL fabricateNewURL()
+ throws InvalidObjectException {
+ // create URL string from deserialized object
+ URL replacementURL = null;
+ String urlString = tempState.reconstituteUrlString();
+
+ try {
+ replacementURL = new URL(urlString);
+ } catch (MalformedURLException mEx) {
+ resetState();
+ InvalidObjectException invoEx = new InvalidObjectException(
+ "Malformed URL: " + urlString);
+ invoEx.initCause(mEx);
+ throw invoEx;
+ }
+ replacementURL.setSerializedHashCode(tempState.getHashCode());
+ resetState();
+ return replacementURL;
+ }
+
+ private boolean isBuiltinStreamHandler(String handlerClassName) {
+ // Android-changed: Some built-in handlers (eg. HttpHandler) are not in sun.net.www.protocol.
+ // return (handlerClassName.startsWith(BUILTIN_HANDLERS_PREFIX));
+ return BUILTIN_HANDLER_CLASS_NAMES.contains(handlerClassName);
+ }
+
+ private void resetState() {
+ this.protocol = null;
+ this.host = null;
+ this.port = -1;
+ this.file = null;
+ this.authority = null;
+ this.ref = null;
+ this.hashCode = -1;
+ this.handler = null;
+ this.query = null;
+ this.path = null;
+ this.userInfo = null;
+ this.tempState = null;
+ }
+
+ private void setSerializedHashCode(int hc) {
+ this.hashCode = hc;
+ }
+}
+
+class Parts {
+ String path, query, ref;
+
+ // Android-changed: App compat. Prepend '/' if host is null / empty.
+ // Parts(String file)
+ Parts(String file, String host) {
+ int ind = file.indexOf('#');
+ ref = ind < 0 ? null: file.substring(ind + 1);
+ file = ind < 0 ? file: file.substring(0, ind);
+ int q = file.lastIndexOf('?');
+ if (q != -1) {
+ query = file.substring(q+1);
+ path = file.substring(0, q);
+ } else {
+ path = file;
+ }
+ // BEGIN Android-changed: App compat. Prepend '/' if host is null / empty.
+ if (path != null && path.length() > 0 && path.charAt(0) != '/' &&
+ host != null && !host.isEmpty()) {
+ path = '/' + path;
+ }
+ // END Android-changed: App compat. Prepend '/' if host is null / empty.
+ }
+
+ String getPath() {
+ return path;
+ }
+
+ String getQuery() {
+ return query;
+ }
+
+ String getRef() {
+ return ref;
+ }
+}
+
+final class UrlDeserializedState {
+ private final String protocol;
+ private final String host;
+ private final int port;
+ private final String authority;
+ private final String file;
+ private final String ref;
+ private final int hashCode;
+
+ public UrlDeserializedState(String protocol,
+ String host, int port,
+ String authority, String file,
+ String ref, int hashCode) {
+ this.protocol = protocol;
+ this.host = host;
+ this.port = port;
+ this.authority = authority;
+ this.file = file;
+ this.ref = ref;
+ this.hashCode = hashCode;
+ }
+
+ String getProtocol() {
+ return protocol;
+ }
+
+ String getHost() {
+ return host;
+ }
+
+ String getAuthority () {
+ return authority;
+ }
+
+ int getPort() {
+ return port;
+ }
+
+ String getFile () {
+ return file;
+ }
+
+ String getRef () {
+ return ref;
+ }
+
+ int getHashCode () {
+ return hashCode;
+ }
+
+ String reconstituteUrlString() {
+
+ // pre-compute length of StringBuilder
+ int len = protocol.length() + 1;
+ if (authority != null && authority.length() > 0)
+ len += 2 + authority.length();
+ if (file != null) {
+ len += file.length();
+ }
+ if (ref != null)
+ len += 1 + ref.length();
+ StringBuilder result = new StringBuilder(len);
+ result.append(protocol);
+ result.append(":");
+ if (authority != null && authority.length() > 0) {
+ result.append("//");
+ result.append(authority);
+ }
+ if (file != null) {
+ result.append(file);
+ }
+ if (ref != null) {
+ result.append("#");
+ result.append(ref);
+ }
+ return result.toString();
+ }
+}
diff --git a/java/net/URLClassLoader.java b/java/net/URLClassLoader.java
new file mode 100644
index 0000000..6e8acb1
--- /dev/null
+++ b/java/net/URLClassLoader.java
@@ -0,0 +1,819 @@
+/*
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.CodeSigner;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.security.SecureClassLoader;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.jar.Attributes;
+import java.util.jar.Attributes.Name;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import sun.misc.Resource;
+import sun.misc.URLClassPath;
+import sun.net.www.ParseUtil;
+import sun.security.util.SecurityConstants;
+
+/**
+ * This class loader is used to load classes and resources from a search
+ * path of URLs referring to both JAR files and directories. Any URL that
+ * ends with a '/' is assumed to refer to a directory. Otherwise, the URL
+ * is assumed to refer to a JAR file which will be opened as needed.
+ * <p>
+ * The AccessControlContext of the thread that created the instance of
+ * URLClassLoader will be used when subsequently loading classes and
+ * resources.
+ * <p>
+ * The classes that are loaded are by default granted permission only to
+ * access the URLs specified when the URLClassLoader was created.
+ *
+ * @author David Connelly
+ * @since 1.2
+ */
+public class URLClassLoader extends SecureClassLoader implements Closeable {
+ /* The search path for classes and resources */
+ private final URLClassPath ucp;
+
+ /* The context to be used when loading classes and resources */
+ private final AccessControlContext acc;
+
+ /**
+ * Constructs a new URLClassLoader for the given URLs. The URLs will be
+ * searched in the order specified for classes and resources after first
+ * searching in the specified parent class loader. Any URL that ends with
+ * a '/' is assumed to refer to a directory. Otherwise, the URL is assumed
+ * to refer to a JAR file which will be downloaded and opened as needed.
+ *
+ * <p>If there is a security manager, this method first
+ * calls the security manager's {@code checkCreateClassLoader} method
+ * to ensure creation of a class loader is allowed.
+ *
+ * @param urls the URLs from which to load classes and resources
+ * @param parent the parent class loader for delegation
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkCreateClassLoader} method doesn't allow
+ * creation of a class loader.
+ * @exception NullPointerException if {@code urls} is {@code null}.
+ * @see SecurityManager#checkCreateClassLoader
+ */
+ public URLClassLoader(URL[] urls, ClassLoader parent) {
+ super(parent);
+ // this is to make the stack depth consistent with 1.1
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkCreateClassLoader();
+ }
+ this.acc = AccessController.getContext();
+ ucp = new URLClassPath(urls, acc);
+ }
+
+ URLClassLoader(URL[] urls, ClassLoader parent,
+ AccessControlContext acc) {
+ super(parent);
+ // this is to make the stack depth consistent with 1.1
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkCreateClassLoader();
+ }
+ this.acc = acc;
+ ucp = new URLClassPath(urls, acc);
+ }
+
+ /**
+ * Constructs a new URLClassLoader for the specified URLs using the
+ * default delegation parent {@code ClassLoader}. The URLs will
+ * be searched in the order specified for classes and resources after
+ * first searching in the parent class loader. Any URL that ends with
+ * a '/' is assumed to refer to a directory. Otherwise, the URL is
+ * assumed to refer to a JAR file which will be downloaded and opened
+ * as needed.
+ *
+ * <p>If there is a security manager, this method first
+ * calls the security manager's {@code checkCreateClassLoader} method
+ * to ensure creation of a class loader is allowed.
+ *
+ * @param urls the URLs from which to load classes and resources
+ *
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkCreateClassLoader} method doesn't allow
+ * creation of a class loader.
+ * @exception NullPointerException if {@code urls} is {@code null}.
+ * @see SecurityManager#checkCreateClassLoader
+ */
+ public URLClassLoader(URL[] urls) {
+ super();
+ // this is to make the stack depth consistent with 1.1
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkCreateClassLoader();
+ }
+ this.acc = AccessController.getContext();
+ ucp = new URLClassPath(urls, acc);
+ }
+
+ URLClassLoader(URL[] urls, AccessControlContext acc) {
+ super();
+ // this is to make the stack depth consistent with 1.1
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkCreateClassLoader();
+ }
+ this.acc = acc;
+ ucp = new URLClassPath(urls, acc);
+ }
+
+ /**
+ * Constructs a new URLClassLoader for the specified URLs, parent
+ * class loader, and URLStreamHandlerFactory. The parent argument
+ * will be used as the parent class loader for delegation. The
+ * factory argument will be used as the stream handler factory to
+ * obtain protocol handlers when creating new jar URLs.
+ *
+ * <p>If there is a security manager, this method first
+ * calls the security manager's {@code checkCreateClassLoader} method
+ * to ensure creation of a class loader is allowed.
+ *
+ * @param urls the URLs from which to load classes and resources
+ * @param parent the parent class loader for delegation
+ * @param factory the URLStreamHandlerFactory to use when creating URLs
+ *
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkCreateClassLoader} method doesn't allow
+ * creation of a class loader.
+ * @exception NullPointerException if {@code urls} is {@code null}.
+ * @see SecurityManager#checkCreateClassLoader
+ */
+ public URLClassLoader(URL[] urls, ClassLoader parent,
+ URLStreamHandlerFactory factory) {
+ super(parent);
+ // this is to make the stack depth consistent with 1.1
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkCreateClassLoader();
+ }
+ acc = AccessController.getContext();
+ ucp = new URLClassPath(urls, factory, acc);
+ }
+
+ /* A map (used as a set) to keep track of closeable local resources
+ * (either JarFiles or FileInputStreams). We don't care about
+ * Http resources since they don't need to be closed.
+ *
+ * If the resource is coming from a jar file
+ * we keep a (weak) reference to the JarFile object which can
+ * be closed if URLClassLoader.close() called. Due to jar file
+ * caching there will typically be only one JarFile object
+ * per underlying jar file.
+ *
+ * For file resources, which is probably a less common situation
+ * we have to keep a weak reference to each stream.
+ */
+
+ private WeakHashMap<Closeable,Void>
+ closeables = new WeakHashMap<>();
+
+ /**
+ * Returns an input stream for reading the specified resource.
+ * If this loader is closed, then any resources opened by this method
+ * will be closed.
+ *
+ * <p> The search order is described in the documentation for {@link
+ * #getResource(String)}. </p>
+ *
+ * @param name
+ * The resource name
+ *
+ * @return An input stream for reading the resource, or {@code null}
+ * if the resource could not be found
+ *
+ * @since 1.7
+ */
+ public InputStream getResourceAsStream(String name) {
+ URL url = getResource(name);
+ try {
+ if (url == null) {
+ return null;
+ }
+ URLConnection urlc = url.openConnection();
+ InputStream is = urlc.getInputStream();
+ if (urlc instanceof JarURLConnection) {
+ JarURLConnection juc = (JarURLConnection)urlc;
+ JarFile jar = juc.getJarFile();
+ synchronized (closeables) {
+ if (!closeables.containsKey(jar)) {
+ closeables.put(jar, null);
+ }
+ }
+ } else if (urlc instanceof sun.net.www.protocol.file.FileURLConnection) {
+ synchronized (closeables) {
+ closeables.put(is, null);
+ }
+ }
+ return is;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Closes this URLClassLoader, so that it can no longer be used to load
+ * new classes or resources that are defined by this loader.
+ * Classes and resources defined by any of this loader's parents in the
+ * delegation hierarchy are still accessible. Also, any classes or resources
+ * that are already loaded, are still accessible.
+ * <p>
+ * In the case of jar: and file: URLs, it also closes any files
+ * that were opened by it. If another thread is loading a
+ * class when the {@code close} method is invoked, then the result of
+ * that load is undefined.
+ * <p>
+ * The method makes a best effort attempt to close all opened files,
+ * by catching {@link IOException}s internally. Unchecked exceptions
+ * and errors are not caught. Calling close on an already closed
+ * loader has no effect.
+ * <p>
+ * @exception IOException if closing any file opened by this class loader
+ * resulted in an IOException. Any such exceptions are caught internally.
+ * If only one is caught, then it is re-thrown. If more than one exception
+ * is caught, then the second and following exceptions are added
+ * as suppressed exceptions of the first one caught, which is then re-thrown.
+ *
+ * @exception SecurityException if a security manager is set, and it denies
+ * {@link RuntimePermission}{@code ("closeClassLoader")}
+ *
+ * @since 1.7
+ */
+ public void close() throws IOException {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkPermission(new RuntimePermission("closeClassLoader"));
+ }
+ List<IOException> errors = ucp.closeLoaders();
+
+ // now close any remaining streams.
+
+ synchronized (closeables) {
+ Set<Closeable> keys = closeables.keySet();
+ for (Closeable c : keys) {
+ try {
+ c.close();
+ } catch (IOException ioex) {
+ errors.add(ioex);
+ }
+ }
+ closeables.clear();
+ }
+
+ if (errors.isEmpty()) {
+ return;
+ }
+
+ IOException firstex = errors.remove(0);
+
+ // Suppress any remaining exceptions
+
+ for (IOException error: errors) {
+ firstex.addSuppressed(error);
+ }
+ throw firstex;
+ }
+
+ /**
+ * Appends the specified URL to the list of URLs to search for
+ * classes and resources.
+ * <p>
+ * If the URL specified is {@code null} or is already in the
+ * list of URLs, or if this loader is closed, then invoking this
+ * method has no effect.
+ *
+ * @param url the URL to be added to the search path of URLs
+ */
+ protected void addURL(URL url) {
+ ucp.addURL(url);
+ }
+
+ /**
+ * Returns the search path of URLs for loading classes and resources.
+ * This includes the original list of URLs specified to the constructor,
+ * along with any URLs subsequently appended by the addURL() method.
+ * @return the search path of URLs for loading classes and resources.
+ */
+ public URL[] getURLs() {
+ return ucp.getURLs();
+ }
+
+ /**
+ * Finds and loads the class with the specified name from the URL search
+ * path. Any URLs referring to JAR files are loaded and opened as needed
+ * until the class is found.
+ *
+ * @param name the name of the class
+ * @return the resulting class
+ * @exception ClassNotFoundException if the class could not be found,
+ * or if the loader is closed.
+ * @exception NullPointerException if {@code name} is {@code null}.
+ */
+ protected Class<?> findClass(final String name)
+ throws ClassNotFoundException
+ {
+ final Class<?> result;
+ try {
+ result = AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Class<?>>() {
+ public Class<?> run() throws ClassNotFoundException {
+ String path = name.replace('.', '/').concat(".class");
+ Resource res = ucp.getResource(path, false);
+ if (res != null) {
+ try {
+ return defineClass(name, res);
+ } catch (IOException e) {
+ throw new ClassNotFoundException(name, e);
+ }
+ } else {
+ return null;
+ }
+ }
+ }, acc);
+ } catch (java.security.PrivilegedActionException pae) {
+ throw (ClassNotFoundException) pae.getException();
+ }
+ if (result == null) {
+ throw new ClassNotFoundException(name);
+ }
+ return result;
+ }
+
+ /*
+ * Retrieve the package using the specified package name.
+ * If non-null, verify the package using the specified code
+ * source and manifest.
+ */
+ private Package getAndVerifyPackage(String pkgname,
+ Manifest man, URL url) {
+ Package pkg = getPackage(pkgname);
+ if (pkg != null) {
+ // Package found, so check package sealing.
+ if (pkg.isSealed()) {
+ // Verify that code source URL is the same.
+ if (!pkg.isSealed(url)) {
+ throw new SecurityException(
+ "sealing violation: package " + pkgname + " is sealed");
+ }
+ } else {
+ // Make sure we are not attempting to seal the package
+ // at this code source URL.
+ if ((man != null) && isSealed(pkgname, man)) {
+ throw new SecurityException(
+ "sealing violation: can't seal package " + pkgname +
+ ": already loaded");
+ }
+ }
+ }
+ return pkg;
+ }
+
+ // Also called by VM to define Package for classes loaded from the CDS
+ // archive
+ private void definePackageInternal(String pkgname, Manifest man, URL url)
+ {
+ if (getAndVerifyPackage(pkgname, man, url) == null) {
+ try {
+ if (man != null) {
+ definePackage(pkgname, man, url);
+ } else {
+ definePackage(pkgname, null, null, null, null, null, null, null);
+ }
+ } catch (IllegalArgumentException iae) {
+ // parallel-capable class loaders: re-verify in case of a
+ // race condition
+ if (getAndVerifyPackage(pkgname, man, url) == null) {
+ // Should never happen
+ throw new AssertionError("Cannot find package " +
+ pkgname);
+ }
+ }
+ }
+ }
+
+ /*
+ * Defines a Class using the class bytes obtained from the specified
+ * Resource. The resulting Class must be resolved before it can be
+ * used.
+ */
+ private Class<?> defineClass(String name, Resource res) throws IOException {
+ long t0 = System.nanoTime();
+ int i = name.lastIndexOf('.');
+ URL url = res.getCodeSourceURL();
+ if (i != -1) {
+ String pkgname = name.substring(0, i);
+ // Check if package already loaded.
+ Manifest man = res.getManifest();
+ definePackageInternal(pkgname, man, url);
+ }
+ // Now read the class bytes and define the class
+ java.nio.ByteBuffer bb = res.getByteBuffer();
+ if (bb != null) {
+ // Use (direct) ByteBuffer:
+ CodeSigner[] signers = res.getCodeSigners();
+ CodeSource cs = new CodeSource(url, signers);
+ // Android-removed: Android doesn't use sun.misc.PerfCounter.
+ // sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
+ return defineClass(name, bb, cs);
+ } else {
+ byte[] b = res.getBytes();
+ // must read certificates AFTER reading bytes.
+ CodeSigner[] signers = res.getCodeSigners();
+ CodeSource cs = new CodeSource(url, signers);
+ // Android-removed: Android doesn't use sun.misc.PerfCounter.
+ // sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
+ return defineClass(name, b, 0, b.length, cs);
+ }
+ }
+
+ /**
+ * Defines a new package by name in this ClassLoader. The attributes
+ * contained in the specified Manifest will be used to obtain package
+ * version and sealing information. For sealed packages, the additional
+ * URL specifies the code source URL from which the package was loaded.
+ *
+ * @param name the package name
+ * @param man the Manifest containing package version and sealing
+ * information
+ * @param url the code source url for the package, or null if none
+ * @exception IllegalArgumentException if the package name duplicates
+ * an existing package either in this class loader or one
+ * of its ancestors
+ * @return the newly defined Package object
+ */
+ protected Package definePackage(String name, Manifest man, URL url)
+ throws IllegalArgumentException
+ {
+ String path = name.replace('.', '/').concat("/");
+ String specTitle = null, specVersion = null, specVendor = null;
+ String implTitle = null, implVersion = null, implVendor = null;
+ String sealed = null;
+ URL sealBase = null;
+
+ Attributes attr = man.getAttributes(path);
+ if (attr != null) {
+ specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
+ specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
+ specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
+ implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
+ implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
+ implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
+ sealed = attr.getValue(Name.SEALED);
+ }
+ attr = man.getMainAttributes();
+ if (attr != null) {
+ if (specTitle == null) {
+ specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
+ }
+ if (specVersion == null) {
+ specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
+ }
+ if (specVendor == null) {
+ specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
+ }
+ if (implTitle == null) {
+ implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
+ }
+ if (implVersion == null) {
+ implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
+ }
+ if (implVendor == null) {
+ implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
+ }
+ if (sealed == null) {
+ sealed = attr.getValue(Name.SEALED);
+ }
+ }
+ if ("true".equalsIgnoreCase(sealed)) {
+ sealBase = url;
+ }
+ return definePackage(name, specTitle, specVersion, specVendor,
+ implTitle, implVersion, implVendor, sealBase);
+ }
+
+ /*
+ * Returns true if the specified package name is sealed according to the
+ * given manifest.
+ */
+ private boolean isSealed(String name, Manifest man) {
+ String path = name.replace('.', '/').concat("/");
+ Attributes attr = man.getAttributes(path);
+ String sealed = null;
+ if (attr != null) {
+ sealed = attr.getValue(Name.SEALED);
+ }
+ if (sealed == null) {
+ if ((attr = man.getMainAttributes()) != null) {
+ sealed = attr.getValue(Name.SEALED);
+ }
+ }
+ return "true".equalsIgnoreCase(sealed);
+ }
+
+ /**
+ * Finds the resource with the specified name on the URL search path.
+ *
+ * @param name the name of the resource
+ * @return a {@code URL} for the resource, or {@code null}
+ * if the resource could not be found, or if the loader is closed.
+ */
+ public URL findResource(final String name) {
+ /*
+ * The same restriction to finding classes applies to resources
+ */
+ URL url = AccessController.doPrivileged(
+ new PrivilegedAction<URL>() {
+ public URL run() {
+ return ucp.findResource(name, true);
+ }
+ }, acc);
+
+ return url != null ? ucp.checkURL(url) : null;
+ }
+
+ /**
+ * Returns an Enumeration of URLs representing all of the resources
+ * on the URL search path having the specified name.
+ *
+ * @param name the resource name
+ * @exception IOException if an I/O exception occurs
+ * @return an {@code Enumeration} of {@code URL}s
+ * If the loader is closed, the Enumeration will be empty.
+ */
+ public Enumeration<URL> findResources(final String name)
+ throws IOException
+ {
+ final Enumeration<URL> e = ucp.findResources(name, true);
+
+ return new Enumeration<URL>() {
+ private URL url = null;
+
+ private boolean next() {
+ if (url != null) {
+ return true;
+ }
+ do {
+ URL u = AccessController.doPrivileged(
+ new PrivilegedAction<URL>() {
+ public URL run() {
+ if (!e.hasMoreElements())
+ return null;
+ return e.nextElement();
+ }
+ }, acc);
+ if (u == null)
+ break;
+ url = ucp.checkURL(u);
+ } while (url == null);
+ return url != null;
+ }
+
+ public URL nextElement() {
+ if (!next()) {
+ throw new NoSuchElementException();
+ }
+ URL u = url;
+ url = null;
+ return u;
+ }
+
+ public boolean hasMoreElements() {
+ return next();
+ }
+ };
+ }
+
+ /**
+ * Returns the permissions for the given codesource object.
+ * The implementation of this method first calls super.getPermissions
+ * and then adds permissions based on the URL of the codesource.
+ * <p>
+ * If the protocol of this URL is "jar", then the permission granted
+ * is based on the permission that is required by the URL of the Jar
+ * file.
+ * <p>
+ * If the protocol is "file" and there is an authority component, then
+ * permission to connect to and accept connections from that authority
+ * may be granted. If the protocol is "file"
+ * and the path specifies a file, then permission to read that
+ * file is granted. If protocol is "file" and the path is
+ * a directory, permission is granted to read all files
+ * and (recursively) all files and subdirectories contained in
+ * that directory.
+ * <p>
+ * If the protocol is not "file", then permission
+ * to connect to and accept connections from the URL's host is granted.
+ * @param codesource the codesource
+ * @exception NullPointerException if {@code codesource} is {@code null}.
+ * @return the permissions granted to the codesource
+ */
+ protected PermissionCollection getPermissions(CodeSource codesource)
+ {
+ PermissionCollection perms = super.getPermissions(codesource);
+
+ URL url = codesource.getLocation();
+
+ Permission p;
+ URLConnection urlConnection;
+
+ try {
+ urlConnection = url.openConnection();
+ p = urlConnection.getPermission();
+ } catch (java.io.IOException ioe) {
+ p = null;
+ urlConnection = null;
+ }
+
+ if (p instanceof FilePermission) {
+ // if the permission has a separator char on the end,
+ // it means the codebase is a directory, and we need
+ // to add an additional permission to read recursively
+ String path = p.getName();
+ if (path.endsWith(File.separator)) {
+ path += "-";
+ p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
+ }
+ } else if ((p == null) && (url.getProtocol().equals("file"))) {
+ String path = url.getFile().replace('/', File.separatorChar);
+ path = ParseUtil.decode(path);
+ if (path.endsWith(File.separator))
+ path += "-";
+ p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
+ } else {
+ /**
+ * Not loading from a 'file:' URL so we want to give the class
+ * permission to connect to and accept from the remote host
+ * after we've made sure the host is the correct one and is valid.
+ */
+ URL locUrl = url;
+ if (urlConnection instanceof JarURLConnection) {
+ locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
+ }
+ String host = locUrl.getHost();
+ if (host != null && (host.length() > 0))
+ p = new SocketPermission(host,
+ SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION);
+ }
+
+ // make sure the person that created this class loader
+ // would have this permission
+
+ if (p != null) {
+ final SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ final Permission fp = p;
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() throws SecurityException {
+ sm.checkPermission(fp);
+ return null;
+ }
+ }, acc);
+ }
+ perms.add(p);
+ }
+ return perms;
+ }
+
+ /**
+ * Creates a new instance of URLClassLoader for the specified
+ * URLs and parent class loader. If a security manager is
+ * installed, the {@code loadClass} method of the URLClassLoader
+ * returned by this method will invoke the
+ * {@code SecurityManager.checkPackageAccess} method before
+ * loading the class.
+ *
+ * @param urls the URLs to search for classes and resources
+ * @param parent the parent class loader for delegation
+ * @exception NullPointerException if {@code urls} is {@code null}.
+ * @return the resulting class loader
+ */
+ public static URLClassLoader newInstance(final URL[] urls,
+ final ClassLoader parent) {
+ // Save the caller's context
+ final AccessControlContext acc = AccessController.getContext();
+ // Need a privileged block to create the class loader
+ URLClassLoader ucl = AccessController.doPrivileged(
+ new PrivilegedAction<URLClassLoader>() {
+ public URLClassLoader run() {
+ return new FactoryURLClassLoader(urls, parent, acc);
+ }
+ });
+ return ucl;
+ }
+
+ /**
+ * Creates a new instance of URLClassLoader for the specified
+ * URLs and default parent class loader. If a security manager is
+ * installed, the {@code loadClass} method of the URLClassLoader
+ * returned by this method will invoke the
+ * {@code SecurityManager.checkPackageAccess} before
+ * loading the class.
+ *
+ * @param urls the URLs to search for classes and resources
+ * @exception NullPointerException if {@code urls} is {@code null}.
+ * @return the resulting class loader
+ */
+ public static URLClassLoader newInstance(final URL[] urls) {
+ // Save the caller's context
+ final AccessControlContext acc = AccessController.getContext();
+ // Need a privileged block to create the class loader
+ URLClassLoader ucl = AccessController.doPrivileged(
+ new PrivilegedAction<URLClassLoader>() {
+ public URLClassLoader run() {
+ return new FactoryURLClassLoader(urls, acc);
+ }
+ });
+ return ucl;
+ }
+
+ static {
+ // Android-removed: SharedSecrets.setJavaNetAccess call. Android doesn't use it.
+ /*sun.misc.SharedSecrets.setJavaNetAccess (
+ new sun.misc.JavaNetAccess() {
+ public URLClassPath getURLClassPath (URLClassLoader u) {
+ return u.ucp;
+ }
+
+ public String getOriginalHostName(InetAddress ia) {
+ return ia.holder.getOriginalHostName();
+ }
+ }
+ );*/
+ ClassLoader.registerAsParallelCapable();
+ }
+}
+
+final class FactoryURLClassLoader extends URLClassLoader {
+
+ static {
+ ClassLoader.registerAsParallelCapable();
+ }
+
+ FactoryURLClassLoader(URL[] urls, ClassLoader parent,
+ AccessControlContext acc) {
+ super(urls, parent, acc);
+ }
+
+ FactoryURLClassLoader(URL[] urls, AccessControlContext acc) {
+ super(urls, acc);
+ }
+
+ public final Class<?> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException
+ {
+ // First check if we have permission to access the package. This
+ // should go away once we've added support for exported packages.
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ int i = name.lastIndexOf('.');
+ if (i != -1) {
+ sm.checkPackageAccess(name.substring(0, i));
+ }
+ }
+ return super.loadClass(name, resolve);
+ }
+}
diff --git a/java/net/URLConnection.java b/java/net/URLConnection.java
new file mode 100644
index 0000000..55ec26d
--- /dev/null
+++ b/java/net/URLConnection.java
@@ -0,0 +1,1820 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Hashtable;
+import java.util.Date;
+import java.util.StringTokenizer;
+import java.util.Collections;
+import java.util.Map;
+import java.util.List;
+import java.security.Permission;
+import java.security.AccessController;
+import sun.security.util.SecurityConstants;
+import sun.net.www.MessageHeader;
+
+/**
+ * The abstract class {@code URLConnection} is the superclass
+ * of all classes that represent a communications link between the
+ * application and a URL. Instances of this class can be used both to
+ * read from and to write to the resource referenced by the URL. In
+ * general, creating a connection to a URL is a multistep process:
+ *
+ * <center><table border=2 summary="Describes the process of creating a connection to a URL: openConnection() and connect() over time.">
+ * <tr><th>{@code openConnection()}</th>
+ * <th>{@code connect()}</th></tr>
+ * <tr><td>Manipulate parameters that affect the connection to the remote
+ * resource.</td>
+ * <td>Interact with the resource; query header fields and
+ * contents.</td></tr>
+ * </table>
+ * ---------------------------->
+ * <br>time</center>
+ *
+ * <ol>
+ * <li>The connection object is created by invoking the
+ * {@code openConnection} method on a URL.
+ * <li>The setup parameters and general request properties are manipulated.
+ * <li>The actual connection to the remote object is made, using the
+ * {@code connect} method.
+ * <li>The remote object becomes available. The header fields and the contents
+ * of the remote object can be accessed.
+ * </ol>
+ * <p>
+ * The setup parameters are modified using the following methods:
+ * <ul>
+ * <li>{@code setAllowUserInteraction}
+ * <li>{@code setDoInput}
+ * <li>{@code setDoOutput}
+ * <li>{@code setIfModifiedSince}
+ * <li>{@code setUseCaches}
+ * </ul>
+ * <p>
+ * and the general request properties are modified using the method:
+ * <ul>
+ * <li>{@code setRequestProperty}
+ * </ul>
+ * <p>
+ * Default values for the {@code AllowUserInteraction} and
+ * {@code UseCaches} parameters can be set using the methods
+ * {@code setDefaultAllowUserInteraction} and
+ * {@code setDefaultUseCaches}.
+ * <p>
+ * Each of the above {@code set} methods has a corresponding
+ * {@code get} method to retrieve the value of the parameter or
+ * general request property. The specific parameters and general
+ * request properties that are applicable are protocol specific.
+ * <p>
+ * The following methods are used to access the header fields and
+ * the contents after the connection is made to the remote object:
+ * <ul>
+ * <li>{@code getContent}
+ * <li>{@code getHeaderField}
+ * <li>{@code getInputStream}
+ * <li>{@code getOutputStream}
+ * </ul>
+ * <p>
+ * Certain header fields are accessed frequently. The methods:
+ * <ul>
+ * <li>{@code getContentEncoding}
+ * <li>{@code getContentLength}
+ * <li>{@code getContentType}
+ * <li>{@code getDate}
+ * <li>{@code getExpiration}
+ * <li>{@code getLastModifed}
+ * </ul>
+ * <p>
+ * provide convenient access to these fields. The
+ * {@code getContentType} method is used by the
+ * {@code getContent} method to determine the type of the remote
+ * object; subclasses may find it convenient to override the
+ * {@code getContentType} method.
+ * <p>
+ * In the common case, all of the pre-connection parameters and
+ * general request properties can be ignored: the pre-connection
+ * parameters and request properties default to sensible values. For
+ * most clients of this interface, there are only two interesting
+ * methods: {@code getInputStream} and {@code getContent},
+ * which are mirrored in the {@code URL} class by convenience methods.
+ * <p>
+ * More information on the request properties and header fields of
+ * an {@code http} connection can be found at:
+ * <blockquote><pre>
+ * <a href="http://www.ietf.org/rfc/rfc2616.txt">http://www.ietf.org/rfc/rfc2616.txt</a>
+ * </pre></blockquote>
+ *
+ * Invoking the {@code close()} methods on the {@code InputStream} or {@code OutputStream} of an
+ * {@code URLConnection} after a request may free network resources associated with this
+ * instance, unless particular protocol specifications specify different behaviours
+ * for it.
+ *
+ * @author James Gosling
+ * @see java.net.URL#openConnection()
+ * @see java.net.URLConnection#connect()
+ * @see java.net.URLConnection#getContent()
+ * @see java.net.URLConnection#getContentEncoding()
+ * @see java.net.URLConnection#getContentLength()
+ * @see java.net.URLConnection#getContentType()
+ * @see java.net.URLConnection#getDate()
+ * @see java.net.URLConnection#getExpiration()
+ * @see java.net.URLConnection#getHeaderField(int)
+ * @see java.net.URLConnection#getHeaderField(java.lang.String)
+ * @see java.net.URLConnection#getInputStream()
+ * @see java.net.URLConnection#getLastModified()
+ * @see java.net.URLConnection#getOutputStream()
+ * @see java.net.URLConnection#setAllowUserInteraction(boolean)
+ * @see java.net.URLConnection#setDefaultUseCaches(boolean)
+ * @see java.net.URLConnection#setDoInput(boolean)
+ * @see java.net.URLConnection#setDoOutput(boolean)
+ * @see java.net.URLConnection#setIfModifiedSince(long)
+ * @see java.net.URLConnection#setRequestProperty(java.lang.String, java.lang.String)
+ * @see java.net.URLConnection#setUseCaches(boolean)
+ * @since JDK1.0
+ */
+public abstract class URLConnection {
+
+ /**
+ * The URL represents the remote object on the World Wide Web to
+ * which this connection is opened.
+ * <p>
+ * The value of this field can be accessed by the
+ * {@code getURL} method.
+ * <p>
+ * The default value of this variable is the value of the URL
+ * argument in the {@code URLConnection} constructor.
+ *
+ * @see java.net.URLConnection#getURL()
+ * @see java.net.URLConnection#url
+ */
+ protected URL url;
+
+ /**
+ * This variable is set by the {@code setDoInput} method. Its
+ * value is returned by the {@code getDoInput} method.
+ * <p>
+ * A URL connection can be used for input and/or output. Setting the
+ * {@code doInput} flag to {@code true} indicates that
+ * the application intends to read data from the URL connection.
+ * <p>
+ * The default value of this field is {@code true}.
+ *
+ * @see java.net.URLConnection#getDoInput()
+ * @see java.net.URLConnection#setDoInput(boolean)
+ */
+ protected boolean doInput = true;
+
+ /**
+ * This variable is set by the {@code setDoOutput} method. Its
+ * value is returned by the {@code getDoOutput} method.
+ * <p>
+ * A URL connection can be used for input and/or output. Setting the
+ * {@code doOutput} flag to {@code true} indicates
+ * that the application intends to write data to the URL connection.
+ * <p>
+ * The default value of this field is {@code false}.
+ *
+ * @see java.net.URLConnection#getDoOutput()
+ * @see java.net.URLConnection#setDoOutput(boolean)
+ */
+ protected boolean doOutput = false;
+
+ private static boolean defaultAllowUserInteraction = false;
+
+ /**
+ * If {@code true}, this {@code URL} is being examined in
+ * a context in which it makes sense to allow user interactions such
+ * as popping up an authentication dialog. If {@code false},
+ * then no user interaction is allowed.
+ * <p>
+ * The value of this field can be set by the
+ * {@code setAllowUserInteraction} method.
+ * Its value is returned by the
+ * {@code getAllowUserInteraction} method.
+ * Its default value is the value of the argument in the last invocation
+ * of the {@code setDefaultAllowUserInteraction} method.
+ *
+ * @see java.net.URLConnection#getAllowUserInteraction()
+ * @see java.net.URLConnection#setAllowUserInteraction(boolean)
+ * @see java.net.URLConnection#setDefaultAllowUserInteraction(boolean)
+ */
+ protected boolean allowUserInteraction = defaultAllowUserInteraction;
+
+ private static boolean defaultUseCaches = true;
+
+ /**
+ * If {@code true}, the protocol is allowed to use caching
+ * whenever it can. If {@code false}, the protocol must always
+ * try to get a fresh copy of the object.
+ * <p>
+ * This field is set by the {@code setUseCaches} method. Its
+ * value is returned by the {@code getUseCaches} method.
+ * <p>
+ * Its default value is the value given in the last invocation of the
+ * {@code setDefaultUseCaches} method.
+ *
+ * @see java.net.URLConnection#setUseCaches(boolean)
+ * @see java.net.URLConnection#getUseCaches()
+ * @see java.net.URLConnection#setDefaultUseCaches(boolean)
+ */
+ protected boolean useCaches = defaultUseCaches;
+
+ /**
+ * Some protocols support skipping the fetching of the object unless
+ * the object has been modified more recently than a certain time.
+ * <p>
+ * A nonzero value gives a time as the number of milliseconds since
+ * January 1, 1970, GMT. The object is fetched only if it has been
+ * modified more recently than that time.
+ * <p>
+ * This variable is set by the {@code setIfModifiedSince}
+ * method. Its value is returned by the
+ * {@code getIfModifiedSince} method.
+ * <p>
+ * The default value of this field is {@code 0}, indicating
+ * that the fetching must always occur.
+ *
+ * @see java.net.URLConnection#getIfModifiedSince()
+ * @see java.net.URLConnection#setIfModifiedSince(long)
+ */
+ protected long ifModifiedSince = 0;
+
+ /**
+ * If {@code false}, this connection object has not created a
+ * communications link to the specified URL. If {@code true},
+ * the communications link has been established.
+ */
+ protected boolean connected = false;
+
+ /**
+ * @since 1.5
+ */
+ private int connectTimeout;
+ private int readTimeout;
+
+ /**
+ * @since 1.6
+ */
+ private MessageHeader requests;
+
+ /**
+ * @since JDK1.1
+ */
+ private static FileNameMap fileNameMap;
+
+ // BEGIN Android-changed: Android has its own mime table.
+ /*
+ /**
+ * @since 1.2.2
+ *
+ private static boolean fileNameMapLoaded = false;
+
+ /**
+ * Loads filename map (a mimetable) from a data file. It will
+ * first try to load the user-specific table, defined
+ * by "content.types.user.table" property. If that fails,
+ * it tries to load the default built-in table.
+ *
+ * @return the FileNameMap
+ * @since 1.2
+ * @see #setFileNameMap(java.net.FileNameMap)
+ *
+ public static synchronized FileNameMap getFileNameMap() {
+ if ((fileNameMap == null) && !fileNameMapLoaded) {
+ fileNameMap = sun.net.www.MimeTable.loadTable();
+ fileNameMapLoaded = true;
+ }
+
+ return new FileNameMap() {
+ private FileNameMap map = fileNameMap;
+ public String getContentTypeFor(String fileName) {
+ return map.getContentTypeFor(fileName);
+ }
+ };
+ }
+ */
+ /**
+ * Returns a {@link FileNameMap} implementation suitable for guessing a
+ * content type based on a URL's "file" component.
+ *
+ * @see #guessContentTypeFromName(String)
+ * @see #setFileNameMap(java.net.FileNameMap)
+ *
+ */
+ public static synchronized FileNameMap getFileNameMap() {
+ if (fileNameMap == null) {
+ fileNameMap = new DefaultFileNameMap();
+ }
+ return fileNameMap;
+ }
+ // END Android-changed: Android has its own mime table.
+
+ /**
+ * Sets the FileNameMap.
+ * <p>
+ * If there is a security manager, this method first calls
+ * the security manager's {@code checkSetFactory} method
+ * to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ *
+ * @param map the FileNameMap to be set
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkSetFactory} method doesn't allow the operation.
+ * @see SecurityManager#checkSetFactory
+ * @see #getFileNameMap()
+ * @since 1.2
+ */
+ public static void setFileNameMap(FileNameMap map) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) sm.checkSetFactory();
+ fileNameMap = map;
+ }
+
+ /**
+ * Opens a communications link to the resource referenced by this
+ * URL, if such a connection has not already been established.
+ * <p>
+ * If the {@code connect} method is called when the connection
+ * has already been opened (indicated by the {@code connected}
+ * field having the value {@code true}), the call is ignored.
+ * <p>
+ * URLConnection objects go through two phases: first they are
+ * created, then they are connected. After being created, and
+ * before being connected, various options can be specified
+ * (e.g., doInput and UseCaches). After connecting, it is an
+ * error to try to set them. Operations that depend on being
+ * connected, like getContentLength, will implicitly perform the
+ * connection, if necessary.
+ *
+ * @throws SocketTimeoutException if the timeout expires before
+ * the connection can be established
+ * @exception IOException if an I/O error occurs while opening the
+ * connection.
+ * @see java.net.URLConnection#connected
+ * @see #getConnectTimeout()
+ * @see #setConnectTimeout(int)
+ */
+ abstract public void connect() throws IOException;
+
+ // Android-changed: Add javadoc to specify Android's timeout behavior.
+ /**
+ * Sets a specified timeout value, in milliseconds, to be used
+ * when opening a communications link to the resource referenced
+ * by this URLConnection. If the timeout expires before the
+ * connection can be established, a
+ * java.net.SocketTimeoutException is raised. A timeout of zero is
+ * interpreted as an infinite timeout.
+
+ * <p> Some non-standard implementation of this method may ignore
+ * the specified timeout. To see the connect timeout set, please
+ * call getConnectTimeout().
+ *
+ * <p><strong>Warning</strong>: If the hostname resolves to multiple IP
+ * addresses, Android's default implementation of {@link HttpURLConnection}
+ * will try each in
+ * <a href="http://www.ietf.org/rfc/rfc3484.txt">RFC 3484</a> order. If
+ * connecting to each of these addresses fails, multiple timeouts will
+ * elapse before the connect attempt throws an exception. Host names
+ * that support both IPv6 and IPv4 always have at least 2 IP addresses.
+ *
+ * @param timeout an {@code int} that specifies the connect
+ * timeout value in milliseconds
+ * @throws IllegalArgumentException if the timeout parameter is negative
+ *
+ * @see #getConnectTimeout()
+ * @see #connect()
+ * @since 1.5
+ */
+ public void setConnectTimeout(int timeout) {
+ if (timeout < 0) {
+ throw new IllegalArgumentException("timeout can not be negative");
+ }
+ connectTimeout = timeout;
+ }
+
+ /**
+ * Returns setting for connect timeout.
+ * <p>
+ * 0 return implies that the option is disabled
+ * (i.e., timeout of infinity).
+ *
+ * @return an {@code int} that indicates the connect timeout
+ * value in milliseconds
+ * @see #setConnectTimeout(int)
+ * @see #connect()
+ * @since 1.5
+ */
+ public int getConnectTimeout() {
+ return connectTimeout;
+ }
+
+ /**
+ * Sets the read timeout to a specified timeout, in
+ * milliseconds. A non-zero value specifies the timeout when
+ * reading from Input stream when a connection is established to a
+ * resource. If the timeout expires before there is data available
+ * for read, a java.net.SocketTimeoutException is raised. A
+ * timeout of zero is interpreted as an infinite timeout.
+ *
+ *<p> Some non-standard implementation of this method ignores the
+ * specified timeout. To see the read timeout set, please call
+ * getReadTimeout().
+ *
+ * @param timeout an {@code int} that specifies the timeout
+ * value to be used in milliseconds
+ * @throws IllegalArgumentException if the timeout parameter is negative
+ *
+ * @see #getReadTimeout()
+ * @see InputStream#read()
+ * @since 1.5
+ */
+ public void setReadTimeout(int timeout) {
+ if (timeout < 0) {
+ throw new IllegalArgumentException("timeout can not be negative");
+ }
+ readTimeout = timeout;
+ }
+
+ /**
+ * Returns setting for read timeout. 0 return implies that the
+ * option is disabled (i.e., timeout of infinity).
+ *
+ * @return an {@code int} that indicates the read timeout
+ * value in milliseconds
+ *
+ * @see #setReadTimeout(int)
+ * @see InputStream#read()
+ * @since 1.5
+ */
+ public int getReadTimeout() {
+ return readTimeout;
+ }
+
+ /**
+ * Constructs a URL connection to the specified URL. A connection to
+ * the object referenced by the URL is not created.
+ *
+ * @param url the specified URL.
+ */
+ protected URLConnection(URL url) {
+ this.url = url;
+ }
+
+ /**
+ * Returns the value of this {@code URLConnection}'s {@code URL}
+ * field.
+ *
+ * @return the value of this {@code URLConnection}'s {@code URL}
+ * field.
+ * @see java.net.URLConnection#url
+ */
+ public URL getURL() {
+ return url;
+ }
+
+ /**
+ * Returns the value of the {@code content-length} header field.
+ * <P>
+ * <B>Note</B>: {@link #getContentLengthLong() getContentLengthLong()}
+ * should be preferred over this method, since it returns a {@code long}
+ * instead and is therefore more portable.</P>
+ *
+ * @return the content length of the resource that this connection's URL
+ * references, {@code -1} if the content length is not known,
+ * or if the content length is greater than Integer.MAX_VALUE.
+ */
+ public int getContentLength() {
+ long l = getContentLengthLong();
+ if (l > Integer.MAX_VALUE)
+ return -1;
+ return (int) l;
+ }
+
+ /**
+ * Returns the value of the {@code content-length} header field as a
+ * long.
+ *
+ * @return the content length of the resource that this connection's URL
+ * references, or {@code -1} if the content length is
+ * not known.
+ * @since 7.0
+ */
+ public long getContentLengthLong() {
+ return getHeaderFieldLong("content-length", -1);
+ }
+
+ /**
+ * Returns the value of the {@code content-type} header field.
+ *
+ * @return the content type of the resource that the URL references,
+ * or {@code null} if not known.
+ * @see java.net.URLConnection#getHeaderField(java.lang.String)
+ */
+ public String getContentType() {
+ return getHeaderField("content-type");
+ }
+
+ /**
+ * Returns the value of the {@code content-encoding} header field.
+ *
+ * @return the content encoding of the resource that the URL references,
+ * or {@code null} if not known.
+ * @see java.net.URLConnection#getHeaderField(java.lang.String)
+ */
+ public String getContentEncoding() {
+ return getHeaderField("content-encoding");
+ }
+
+ /**
+ * Returns the value of the {@code expires} header field.
+ *
+ * @return the expiration date of the resource that this URL references,
+ * or 0 if not known. The value is the number of milliseconds since
+ * January 1, 1970 GMT.
+ * @see java.net.URLConnection#getHeaderField(java.lang.String)
+ */
+ public long getExpiration() {
+ return getHeaderFieldDate("expires", 0);
+ }
+
+ /**
+ * Returns the value of the {@code date} header field.
+ *
+ * @return the sending date of the resource that the URL references,
+ * or {@code 0} if not known. The value returned is the
+ * number of milliseconds since January 1, 1970 GMT.
+ * @see java.net.URLConnection#getHeaderField(java.lang.String)
+ */
+ public long getDate() {
+ return getHeaderFieldDate("date", 0);
+ }
+
+ /**
+ * Returns the value of the {@code last-modified} header field.
+ * The result is the number of milliseconds since January 1, 1970 GMT.
+ *
+ * @return the date the resource referenced by this
+ * {@code URLConnection} was last modified, or 0 if not known.
+ * @see java.net.URLConnection#getHeaderField(java.lang.String)
+ */
+ public long getLastModified() {
+ return getHeaderFieldDate("last-modified", 0);
+ }
+
+ /**
+ * Returns the value of the named header field.
+ * <p>
+ * If called on a connection that sets the same header multiple times
+ * with possibly different values, only the last value is returned.
+ *
+ *
+ * @param name the name of a header field.
+ * @return the value of the named header field, or {@code null}
+ * if there is no such field in the header.
+ */
+ public String getHeaderField(String name) {
+ return null;
+ }
+
+ /**
+ * Returns an unmodifiable Map of the header fields.
+ * The Map keys are Strings that represent the
+ * response-header field names. Each Map value is an
+ * unmodifiable List of Strings that represents
+ * the corresponding field values.
+ *
+ * @return a Map of header fields
+ * @since 1.4
+ */
+ public Map<String,List<String>> getHeaderFields() {
+ return Collections.emptyMap();
+ }
+
+ /**
+ * Returns the value of the named field parsed as a number.
+ * <p>
+ * This form of {@code getHeaderField} exists because some
+ * connection types (e.g., {@code http-ng}) have pre-parsed
+ * headers. Classes for that connection type can override this method
+ * and short-circuit the parsing.
+ *
+ * @param name the name of the header field.
+ * @param Default the default value.
+ * @return the value of the named field, parsed as an integer. The
+ * {@code Default} value is returned if the field is
+ * missing or malformed.
+ */
+ public int getHeaderFieldInt(String name, int Default) {
+ String value = getHeaderField(name);
+ try {
+ return Integer.parseInt(value);
+ } catch (Exception e) { }
+ return Default;
+ }
+
+ /**
+ * Returns the value of the named field parsed as a number.
+ * <p>
+ * This form of {@code getHeaderField} exists because some
+ * connection types (e.g., {@code http-ng}) have pre-parsed
+ * headers. Classes for that connection type can override this method
+ * and short-circuit the parsing.
+ *
+ * @param name the name of the header field.
+ * @param Default the default value.
+ * @return the value of the named field, parsed as a long. The
+ * {@code Default} value is returned if the field is
+ * missing or malformed.
+ * @since 7.0
+ */
+ public long getHeaderFieldLong(String name, long Default) {
+ String value = getHeaderField(name);
+ try {
+ return Long.parseLong(value);
+ } catch (Exception e) { }
+ return Default;
+ }
+
+ /**
+ * Returns the value of the named field parsed as date.
+ * The result is the number of milliseconds since January 1, 1970 GMT
+ * represented by the named field.
+ * <p>
+ * This form of {@code getHeaderField} exists because some
+ * connection types (e.g., {@code http-ng}) have pre-parsed
+ * headers. Classes for that connection type can override this method
+ * and short-circuit the parsing.
+ *
+ * @param name the name of the header field.
+ * @param Default a default value.
+ * @return the value of the field, parsed as a date. The value of the
+ * {@code Default} argument is returned if the field is
+ * missing or malformed.
+ */
+ @SuppressWarnings("deprecation")
+ public long getHeaderFieldDate(String name, long Default) {
+ String value = getHeaderField(name);
+ try {
+ return Date.parse(value);
+ } catch (Exception e) { }
+ return Default;
+ }
+
+ /**
+ * Returns the key for the {@code n}<sup>th</sup> header field.
+ * It returns {@code null} if there are fewer than {@code n+1} fields.
+ *
+ * @param n an index, where {@code n>=0}
+ * @return the key for the {@code n}<sup>th</sup> header field,
+ * or {@code null} if there are fewer than {@code n+1}
+ * fields.
+ */
+ public String getHeaderFieldKey(int n) {
+ return null;
+ }
+
+ /**
+ * Returns the value for the {@code n}<sup>th</sup> header field.
+ * It returns {@code null} if there are fewer than
+ * {@code n+1}fields.
+ * <p>
+ * This method can be used in conjunction with the
+ * {@link #getHeaderFieldKey(int) getHeaderFieldKey} method to iterate through all
+ * the headers in the message.
+ *
+ * @param n an index, where {@code n>=0}
+ * @return the value of the {@code n}<sup>th</sup> header field
+ * or {@code null} if there are fewer than {@code n+1} fields
+ * @see java.net.URLConnection#getHeaderFieldKey(int)
+ */
+ public String getHeaderField(int n) {
+ return null;
+ }
+
+ /**
+ * Retrieves the contents of this URL connection.
+ * <p>
+ * This method first determines the content type of the object by
+ * calling the {@code getContentType} method. If this is
+ * the first time that the application has seen that specific content
+ * type, a content handler for that content type is created:
+ * <ol>
+ * <li>If the application has set up a content handler factory instance
+ * using the {@code setContentHandlerFactory} method, the
+ * {@code createContentHandler} method of that instance is called
+ * with the content type as an argument; the result is a content
+ * handler for that content type.
+ * <li>If no content handler factory has yet been set up, or if the
+ * factory's {@code createContentHandler} method returns
+ * {@code null}, then the application loads the class named:
+ * <blockquote><pre>
+ * sun.net.www.content.<<i>contentType</i>>
+ * </pre></blockquote>
+ * where <<i>contentType</i>> is formed by taking the
+ * content-type string, replacing all slash characters with a
+ * {@code period} ('.'), and all other non-alphanumeric characters
+ * with the underscore character '{@code _}'. The alphanumeric
+ * characters are specifically the 26 uppercase ASCII letters
+ * '{@code A}' through '{@code Z}', the 26 lowercase ASCII
+ * letters '{@code a}' through '{@code z}', and the 10 ASCII
+ * digits '{@code 0}' through '{@code 9}'. If the specified
+ * class does not exist, or is not a subclass of
+ * {@code ContentHandler}, then an
+ * {@code UnknownServiceException} is thrown.
+ * </ol>
+ *
+ * @return the object fetched. The {@code instanceof} operator
+ * should be used to determine the specific kind of object
+ * returned.
+ * @exception IOException if an I/O error occurs while
+ * getting the content.
+ * @exception UnknownServiceException if the protocol does not support
+ * the content type.
+ * @see java.net.ContentHandlerFactory#createContentHandler(java.lang.String)
+ * @see java.net.URLConnection#getContentType()
+ * @see java.net.URLConnection#setContentHandlerFactory(java.net.ContentHandlerFactory)
+ */
+ public Object getContent() throws IOException {
+ // Must call getInputStream before GetHeaderField gets called
+ // so that FileNotFoundException has a chance to be thrown up
+ // from here without being caught.
+ getInputStream();
+ return getContentHandler().getContent(this);
+ }
+
+ /**
+ * Retrieves the contents of this URL connection.
+ *
+ * @param classes the {@code Class} array
+ * indicating the requested types
+ * @return the object fetched that is the first match of the type
+ * specified in the classes array. null if none of
+ * the requested types are supported.
+ * The {@code instanceof} operator should be used to
+ * determine the specific kind of object returned.
+ * @exception IOException if an I/O error occurs while
+ * getting the content.
+ * @exception UnknownServiceException if the protocol does not support
+ * the content type.
+ * @see java.net.URLConnection#getContent()
+ * @see java.net.ContentHandlerFactory#createContentHandler(java.lang.String)
+ * @see java.net.URLConnection#getContent(java.lang.Class[])
+ * @see java.net.URLConnection#setContentHandlerFactory(java.net.ContentHandlerFactory)
+ * @since 1.3
+ */
+ public Object getContent(Class[] classes) throws IOException {
+ // Must call getInputStream before GetHeaderField gets called
+ // so that FileNotFoundException has a chance to be thrown up
+ // from here without being caught.
+ getInputStream();
+ return getContentHandler().getContent(this, classes);
+ }
+
+ /**
+ * Returns a permission object representing the permission
+ * necessary to make the connection represented by this
+ * object. This method returns null if no permission is
+ * required to make the connection. By default, this method
+ * returns {@code java.security.AllPermission}. Subclasses
+ * should override this method and return the permission
+ * that best represents the permission required to make a
+ * a connection to the URL. For example, a {@code URLConnection}
+ * representing a {@code file:} URL would return a
+ * {@code java.io.FilePermission} object.
+ *
+ * <p>The permission returned may dependent upon the state of the
+ * connection. For example, the permission before connecting may be
+ * different from that after connecting. For example, an HTTP
+ * sever, say foo.com, may redirect the connection to a different
+ * host, say bar.com. Before connecting the permission returned by
+ * the connection will represent the permission needed to connect
+ * to foo.com, while the permission returned after connecting will
+ * be to bar.com.
+ *
+ * <p>Permissions are generally used for two purposes: to protect
+ * caches of objects obtained through URLConnections, and to check
+ * the right of a recipient to learn about a particular URL. In
+ * the first case, the permission should be obtained
+ * <em>after</em> the object has been obtained. For example, in an
+ * HTTP connection, this will represent the permission to connect
+ * to the host from which the data was ultimately fetched. In the
+ * second case, the permission should be obtained and tested
+ * <em>before</em> connecting.
+ *
+ * @return the permission object representing the permission
+ * necessary to make the connection represented by this
+ * URLConnection.
+ *
+ * @exception IOException if the computation of the permission
+ * requires network or file I/O and an exception occurs while
+ * computing it.
+ */
+ public Permission getPermission() throws IOException {
+ return SecurityConstants.ALL_PERMISSION;
+ }
+
+ /**
+ * Returns an input stream that reads from this open connection.
+ *
+ * A SocketTimeoutException can be thrown when reading from the
+ * returned input stream if the read timeout expires before data
+ * is available for read.
+ *
+ * @return an input stream that reads from this open connection.
+ * @exception IOException if an I/O error occurs while
+ * creating the input stream.
+ * @exception UnknownServiceException if the protocol does not support
+ * input.
+ * @see #setReadTimeout(int)
+ * @see #getReadTimeout()
+ */
+ public InputStream getInputStream() throws IOException {
+ throw new UnknownServiceException("protocol doesn't support input");
+ }
+
+ /**
+ * Returns an output stream that writes to this connection.
+ *
+ * @return an output stream that writes to this connection.
+ * @exception IOException if an I/O error occurs while
+ * creating the output stream.
+ * @exception UnknownServiceException if the protocol does not support
+ * output.
+ */
+ public OutputStream getOutputStream() throws IOException {
+ throw new UnknownServiceException("protocol doesn't support output");
+ }
+
+ /**
+ * Returns a {@code String} representation of this URL connection.
+ *
+ * @return a string representation of this {@code URLConnection}.
+ */
+ public String toString() {
+ return this.getClass().getName() + ":" + url;
+ }
+
+ /**
+ * Sets the value of the {@code doInput} field for this
+ * {@code URLConnection} to the specified value.
+ * <p>
+ * A URL connection can be used for input and/or output. Set the DoInput
+ * flag to true if you intend to use the URL connection for input,
+ * false if not. The default is true.
+ *
+ * @param doinput the new value.
+ * @throws IllegalStateException if already connected
+ * @see java.net.URLConnection#doInput
+ * @see #getDoInput()
+ */
+ public void setDoInput(boolean doinput) {
+ if (connected)
+ throw new IllegalStateException("Already connected");
+ doInput = doinput;
+ }
+
+ /**
+ * Returns the value of this {@code URLConnection}'s
+ * {@code doInput} flag.
+ *
+ * @return the value of this {@code URLConnection}'s
+ * {@code doInput} flag.
+ * @see #setDoInput(boolean)
+ */
+ public boolean getDoInput() {
+ return doInput;
+ }
+
+ /**
+ * Sets the value of the {@code doOutput} field for this
+ * {@code URLConnection} to the specified value.
+ * <p>
+ * A URL connection can be used for input and/or output. Set the DoOutput
+ * flag to true if you intend to use the URL connection for output,
+ * false if not. The default is false.
+ *
+ * @param dooutput the new value.
+ * @throws IllegalStateException if already connected
+ * @see #getDoOutput()
+ */
+ public void setDoOutput(boolean dooutput) {
+ if (connected)
+ throw new IllegalStateException("Already connected");
+ doOutput = dooutput;
+ }
+
+ /**
+ * Returns the value of this {@code URLConnection}'s
+ * {@code doOutput} flag.
+ *
+ * @return the value of this {@code URLConnection}'s
+ * {@code doOutput} flag.
+ * @see #setDoOutput(boolean)
+ */
+ public boolean getDoOutput() {
+ return doOutput;
+ }
+
+ /**
+ * Set the value of the {@code allowUserInteraction} field of
+ * this {@code URLConnection}.
+ *
+ * @param allowuserinteraction the new value.
+ * @throws IllegalStateException if already connected
+ * @see #getAllowUserInteraction()
+ */
+ public void setAllowUserInteraction(boolean allowuserinteraction) {
+ if (connected)
+ throw new IllegalStateException("Already connected");
+ allowUserInteraction = allowuserinteraction;
+ }
+
+ /**
+ * Returns the value of the {@code allowUserInteraction} field for
+ * this object.
+ *
+ * @return the value of the {@code allowUserInteraction} field for
+ * this object.
+ * @see #setAllowUserInteraction(boolean)
+ */
+ public boolean getAllowUserInteraction() {
+ return allowUserInteraction;
+ }
+
+ /**
+ * Sets the default value of the
+ * {@code allowUserInteraction} field for all future
+ * {@code URLConnection} objects to the specified value.
+ *
+ * @param defaultallowuserinteraction the new value.
+ * @see #getDefaultAllowUserInteraction()
+ */
+ public static void setDefaultAllowUserInteraction(boolean defaultallowuserinteraction) {
+ defaultAllowUserInteraction = defaultallowuserinteraction;
+ }
+
+ /**
+ * Returns the default value of the {@code allowUserInteraction}
+ * field.
+ * <p>
+ * Ths default is "sticky", being a part of the static state of all
+ * URLConnections. This flag applies to the next, and all following
+ * URLConnections that are created.
+ *
+ * @return the default value of the {@code allowUserInteraction}
+ * field.
+ * @see #setDefaultAllowUserInteraction(boolean)
+ */
+ public static boolean getDefaultAllowUserInteraction() {
+ return defaultAllowUserInteraction;
+ }
+
+ /**
+ * Sets the value of the {@code useCaches} field of this
+ * {@code URLConnection} to the specified value.
+ * <p>
+ * Some protocols do caching of documents. Occasionally, it is important
+ * to be able to "tunnel through" and ignore the caches (e.g., the
+ * "reload" button in a browser). If the UseCaches flag on a connection
+ * is true, the connection is allowed to use whatever caches it can.
+ * If false, caches are to be ignored.
+ * The default value comes from DefaultUseCaches, which defaults to
+ * true.
+ *
+ * @param usecaches a {@code boolean} indicating whether
+ * or not to allow caching
+ * @throws IllegalStateException if already connected
+ * @see #getUseCaches()
+ */
+ public void setUseCaches(boolean usecaches) {
+ if (connected)
+ throw new IllegalStateException("Already connected");
+ useCaches = usecaches;
+ }
+
+ /**
+ * Returns the value of this {@code URLConnection}'s
+ * {@code useCaches} field.
+ *
+ * @return the value of this {@code URLConnection}'s
+ * {@code useCaches} field.
+ * @see #setUseCaches(boolean)
+ */
+ public boolean getUseCaches() {
+ return useCaches;
+ }
+
+ /**
+ * Sets the value of the {@code ifModifiedSince} field of
+ * this {@code URLConnection} to the specified value.
+ *
+ * @param ifmodifiedsince the new value.
+ * @throws IllegalStateException if already connected
+ * @see #getIfModifiedSince()
+ */
+ public void setIfModifiedSince(long ifmodifiedsince) {
+ if (connected)
+ throw new IllegalStateException("Already connected");
+ ifModifiedSince = ifmodifiedsince;
+ }
+
+ /**
+ * Returns the value of this object's {@code ifModifiedSince} field.
+ *
+ * @return the value of this object's {@code ifModifiedSince} field.
+ * @see #setIfModifiedSince(long)
+ */
+ public long getIfModifiedSince() {
+ return ifModifiedSince;
+ }
+
+ /**
+ * Returns the default value of a {@code URLConnection}'s
+ * {@code useCaches} flag.
+ * <p>
+ * Ths default is "sticky", being a part of the static state of all
+ * URLConnections. This flag applies to the next, and all following
+ * URLConnections that are created.
+ *
+ * @return the default value of a {@code URLConnection}'s
+ * {@code useCaches} flag.
+ * @see #setDefaultUseCaches(boolean)
+ */
+ public boolean getDefaultUseCaches() {
+ return defaultUseCaches;
+ }
+
+ /**
+ * Sets the default value of the {@code useCaches} field to the
+ * specified value.
+ *
+ * @param defaultusecaches the new value.
+ * @see #getDefaultUseCaches()
+ */
+ public void setDefaultUseCaches(boolean defaultusecaches) {
+ defaultUseCaches = defaultusecaches;
+ }
+
+ /**
+ * Sets the general request property. If a property with the key already
+ * exists, overwrite its value with the new value.
+ *
+ * <p> NOTE: HTTP requires all request properties which can
+ * legally have multiple instances with the same key
+ * to use a comma-separated list syntax which enables multiple
+ * properties to be appended into a single property.
+ *
+ * @param key the keyword by which the request is known
+ * (e.g., "{@code Accept}").
+ * @param value the value associated with it.
+ * @throws IllegalStateException if already connected
+ * @throws NullPointerException if key is <CODE>null</CODE>
+ * @see #getRequestProperty(java.lang.String)
+ */
+ public void setRequestProperty(String key, String value) {
+ if (connected)
+ throw new IllegalStateException("Already connected");
+ if (key == null)
+ throw new NullPointerException ("key is null");
+
+ if (requests == null)
+ requests = new MessageHeader();
+
+ requests.set(key, value);
+ }
+
+ /**
+ * Adds a general request property specified by a
+ * key-value pair. This method will not overwrite
+ * existing values associated with the same key.
+ *
+ * @param key the keyword by which the request is known
+ * (e.g., "{@code Accept}").
+ * @param value the value associated with it.
+ * @throws IllegalStateException if already connected
+ * @throws NullPointerException if key is null
+ * @see #getRequestProperties()
+ * @since 1.4
+ */
+ public void addRequestProperty(String key, String value) {
+ if (connected)
+ throw new IllegalStateException("Already connected");
+ if (key == null)
+ throw new NullPointerException ("key is null");
+
+ if (requests == null)
+ requests = new MessageHeader();
+
+ requests.add(key, value);
+ }
+
+
+ /**
+ * Returns the value of the named general request property for this
+ * connection.
+ *
+ * @param key the keyword by which the request is known (e.g., "Accept").
+ * @return the value of the named general request property for this
+ * connection. If key is null, then null is returned.
+ * @throws IllegalStateException if already connected
+ * @see #setRequestProperty(java.lang.String, java.lang.String)
+ */
+ public String getRequestProperty(String key) {
+ if (connected)
+ throw new IllegalStateException("Already connected");
+
+ if (requests == null)
+ return null;
+
+ return requests.findValue(key);
+ }
+
+ /**
+ * Returns an unmodifiable Map of general request
+ * properties for this connection. The Map keys
+ * are Strings that represent the request-header
+ * field names. Each Map value is a unmodifiable List
+ * of Strings that represents the corresponding
+ * field values.
+ *
+ * @return a Map of the general request properties for this connection.
+ * @throws IllegalStateException if already connected
+ * @since 1.4
+ */
+ public Map<String,List<String>> getRequestProperties() {
+ if (connected)
+ throw new IllegalStateException("Already connected");
+
+ if (requests == null)
+ return Collections.emptyMap();
+
+ return requests.getHeaders(null);
+ }
+
+ /**
+ * Sets the default value of a general request property. When a
+ * {@code URLConnection} is created, it is initialized with
+ * these properties.
+ *
+ * @param key the keyword by which the request is known
+ * (e.g., "{@code Accept}").
+ * @param value the value associated with the key.
+ *
+ * @see java.net.URLConnection#setRequestProperty(java.lang.String,java.lang.String)
+ *
+ * @deprecated The instance specific setRequestProperty method
+ * should be used after an appropriate instance of URLConnection
+ * is obtained. Invoking this method will have no effect.
+ *
+ * @see #getDefaultRequestProperty(java.lang.String)
+ */
+ @Deprecated
+ public static void setDefaultRequestProperty(String key, String value) {
+ }
+
+ /**
+ * Returns the value of the default request property. Default request
+ * properties are set for every connection.
+ *
+ * @param key the keyword by which the request is known (e.g., "Accept").
+ * @return the value of the default request property
+ * for the specified key.
+ *
+ * @see java.net.URLConnection#getRequestProperty(java.lang.String)
+ *
+ * @deprecated The instance specific getRequestProperty method
+ * should be used after an appropriate instance of URLConnection
+ * is obtained.
+ *
+ * @see #setDefaultRequestProperty(java.lang.String, java.lang.String)
+ */
+ @Deprecated
+ public static String getDefaultRequestProperty(String key) {
+ return null;
+ }
+
+ /**
+ * The ContentHandler factory.
+ */
+ static ContentHandlerFactory factory;
+
+ /**
+ * Sets the {@code ContentHandlerFactory} of an
+ * application. It can be called at most once by an application.
+ * <p>
+ * The {@code ContentHandlerFactory} instance is used to
+ * construct a content handler from a content type
+ * <p>
+ * If there is a security manager, this method first calls
+ * the security manager's {@code checkSetFactory} method
+ * to ensure the operation is allowed.
+ * This could result in a SecurityException.
+ *
+ * @param fac the desired factory.
+ * @exception Error if the factory has already been defined.
+ * @exception SecurityException if a security manager exists and its
+ * {@code checkSetFactory} method doesn't allow the operation.
+ * @see java.net.ContentHandlerFactory
+ * @see java.net.URLConnection#getContent()
+ * @see SecurityManager#checkSetFactory
+ */
+ public static synchronized void setContentHandlerFactory(ContentHandlerFactory fac) {
+ if (factory != null) {
+ throw new Error("factory already defined");
+ }
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkSetFactory();
+ }
+ factory = fac;
+ }
+
+ private static Hashtable<String, ContentHandler> handlers = new Hashtable<>();
+
+ /**
+ * Gets the Content Handler appropriate for this connection.
+ */
+ synchronized ContentHandler getContentHandler()
+ throws IOException
+ {
+ String contentType = stripOffParameters(getContentType());
+ ContentHandler handler = null;
+ // BEGIN Android-changed: App Compat. Android guesses content type from name and stream.
+ if (contentType == null) {
+ if ((contentType = guessContentTypeFromName(url.getFile())) == null) {
+ contentType = guessContentTypeFromStream(getInputStream());
+ }
+ }
+
+ if (contentType == null) {
+ return UnknownContentHandler.INSTANCE;
+ }
+ // END Android-changed: App Compat. Android guesses content type from name and stream.
+ try {
+ handler = handlers.get(contentType);
+ if (handler != null)
+ return handler;
+ } catch(Exception e) {
+ }
+
+ if (factory != null)
+ handler = factory.createContentHandler(contentType);
+ if (handler == null) {
+ try {
+ handler = lookupContentHandlerClassFor(contentType);
+ } catch(Exception e) {
+ e.printStackTrace();
+ handler = UnknownContentHandler.INSTANCE;
+ }
+ handlers.put(contentType, handler);
+ }
+ return handler;
+ }
+
+ /*
+ * Media types are in the format: type/subtype*(; parameter).
+ * For looking up the content handler, we should ignore those
+ * parameters.
+ */
+ private String stripOffParameters(String contentType)
+ {
+ if (contentType == null)
+ return null;
+ int index = contentType.indexOf(';');
+
+ if (index > 0)
+ return contentType.substring(0, index);
+ else
+ return contentType;
+ }
+
+ private static final String contentClassPrefix = "sun.net.www.content";
+ private static final String contentPathProp = "java.content.handler.pkgs";
+
+ /**
+ * Looks for a content handler in a user-defineable set of places.
+ * By default it looks in sun.net.www.content, but users can define a
+ * vertical-bar delimited set of class prefixes to search through in
+ * addition by defining the java.content.handler.pkgs property.
+ * The class name must be of the form:
+ * <pre>
+ * {package-prefix}.{major}.{minor}
+ * e.g.
+ * YoyoDyne.experimental.text.plain
+ * </pre>
+ */
+ private ContentHandler lookupContentHandlerClassFor(String contentType)
+ throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ String contentHandlerClassName = typeToPackageName(contentType);
+
+ String contentHandlerPkgPrefixes =getContentHandlerPkgPrefixes();
+
+ StringTokenizer packagePrefixIter =
+ new StringTokenizer(contentHandlerPkgPrefixes, "|");
+
+ while (packagePrefixIter.hasMoreTokens()) {
+ String packagePrefix = packagePrefixIter.nextToken().trim();
+
+ try {
+ String clsName = packagePrefix + "." + contentHandlerClassName;
+ Class<?> cls = null;
+ try {
+ cls = Class.forName(clsName);
+ } catch (ClassNotFoundException e) {
+ ClassLoader cl = ClassLoader.getSystemClassLoader();
+ if (cl != null) {
+ cls = cl.loadClass(clsName);
+ }
+ }
+ if (cls != null) {
+ ContentHandler handler =
+ (ContentHandler)cls.newInstance();
+ return handler;
+ }
+ } catch(Exception e) {
+ }
+ }
+
+ return UnknownContentHandler.INSTANCE;
+ }
+
+ /**
+ * Utility function to map a MIME content type into an equivalent
+ * pair of class name components. For example: "text/html" would
+ * be returned as "text.html"
+ */
+ private String typeToPackageName(String contentType) {
+ // make sure we canonicalize the class name: all lower case
+ contentType = contentType.toLowerCase();
+ int len = contentType.length();
+ char nm[] = new char[len];
+ contentType.getChars(0, len, nm, 0);
+ for (int i = 0; i < len; i++) {
+ char c = nm[i];
+ if (c == '/') {
+ nm[i] = '.';
+ } else if (!('A' <= c && c <= 'Z' ||
+ 'a' <= c && c <= 'z' ||
+ '0' <= c && c <= '9')) {
+ nm[i] = '_';
+ }
+ }
+ return new String(nm);
+ }
+
+
+ /**
+ * Returns a vertical bar separated list of package prefixes for potential
+ * content handlers. Tries to get the java.content.handler.pkgs property
+ * to use as a set of package prefixes to search. Whether or not
+ * that property has been defined, the sun.net.www.content is always
+ * the last one on the returned package list.
+ */
+ private String getContentHandlerPkgPrefixes() {
+ String packagePrefixList = AccessController.doPrivileged(
+ new sun.security.action.GetPropertyAction(contentPathProp, ""));
+
+ if (packagePrefixList != "") {
+ packagePrefixList += "|";
+ }
+
+ return packagePrefixList + contentClassPrefix;
+ }
+
+ /**
+ * Tries to determine the content type of an object, based
+ * on the specified "file" component of a URL.
+ * This is a convenience method that can be used by
+ * subclasses that override the {@code getContentType} method.
+ *
+ * @param fname a filename.
+ * @return a guess as to what the content type of the object is,
+ * based upon its file name.
+ * @see java.net.URLConnection#getContentType()
+ */
+ public static String guessContentTypeFromName(String fname) {
+ return getFileNameMap().getContentTypeFor(fname);
+ }
+
+ /**
+ * Tries to determine the type of an input stream based on the
+ * characters at the beginning of the input stream. This method can
+ * be used by subclasses that override the
+ * {@code getContentType} method.
+ * <p>
+ * Ideally, this routine would not be needed. But many
+ * {@code http} servers return the incorrect content type; in
+ * addition, there are many nonstandard extensions. Direct inspection
+ * of the bytes to determine the content type is often more accurate
+ * than believing the content type claimed by the {@code http} server.
+ *
+ * @param is an input stream that supports marks.
+ * @return a guess at the content type, or {@code null} if none
+ * can be determined.
+ * @exception IOException if an I/O error occurs while reading the
+ * input stream.
+ * @see java.io.InputStream#mark(int)
+ * @see java.io.InputStream#markSupported()
+ * @see java.net.URLConnection#getContentType()
+ */
+ static public String guessContentTypeFromStream(InputStream is)
+ throws IOException {
+ // If we can't read ahead safely, just give up on guessing
+ if (!is.markSupported())
+ return null;
+
+ is.mark(16);
+ int c1 = is.read();
+ int c2 = is.read();
+ int c3 = is.read();
+ int c4 = is.read();
+ int c5 = is.read();
+ int c6 = is.read();
+ int c7 = is.read();
+ int c8 = is.read();
+ int c9 = is.read();
+ int c10 = is.read();
+ int c11 = is.read();
+ int c12 = is.read();
+ int c13 = is.read();
+ int c14 = is.read();
+ int c15 = is.read();
+ int c16 = is.read();
+ is.reset();
+
+ if (c1 == 0xCA && c2 == 0xFE && c3 == 0xBA && c4 == 0xBE) {
+ return "application/java-vm";
+ }
+
+ if (c1 == 0xAC && c2 == 0xED) {
+ // next two bytes are version number, currently 0x00 0x05
+ return "application/x-java-serialized-object";
+ }
+
+ if (c1 == '<') {
+ if (c2 == '!'
+ || ((c2 == 'h' && (c3 == 't' && c4 == 'm' && c5 == 'l' ||
+ c3 == 'e' && c4 == 'a' && c5 == 'd') ||
+ (c2 == 'b' && c3 == 'o' && c4 == 'd' && c5 == 'y'))) ||
+ ((c2 == 'H' && (c3 == 'T' && c4 == 'M' && c5 == 'L' ||
+ c3 == 'E' && c4 == 'A' && c5 == 'D') ||
+ (c2 == 'B' && c3 == 'O' && c4 == 'D' && c5 == 'Y')))) {
+ return "text/html";
+ }
+
+ if (c2 == '?' && c3 == 'x' && c4 == 'm' && c5 == 'l' && c6 == ' ') {
+ return "application/xml";
+ }
+ }
+
+ // big and little (identical) endian UTF-8 encodings, with BOM
+ if (c1 == 0xef && c2 == 0xbb && c3 == 0xbf) {
+ if (c4 == '<' && c5 == '?' && c6 == 'x') {
+ return "application/xml";
+ }
+ }
+
+ // big and little endian UTF-16 encodings, with byte order mark
+ if (c1 == 0xfe && c2 == 0xff) {
+ if (c3 == 0 && c4 == '<' && c5 == 0 && c6 == '?' &&
+ c7 == 0 && c8 == 'x') {
+ return "application/xml";
+ }
+ }
+
+ if (c1 == 0xff && c2 == 0xfe) {
+ if (c3 == '<' && c4 == 0 && c5 == '?' && c6 == 0 &&
+ c7 == 'x' && c8 == 0) {
+ return "application/xml";
+ }
+ }
+
+ // big and little endian UTF-32 encodings, with BOM
+ if (c1 == 0x00 && c2 == 0x00 && c3 == 0xfe && c4 == 0xff) {
+ if (c5 == 0 && c6 == 0 && c7 == 0 && c8 == '<' &&
+ c9 == 0 && c10 == 0 && c11 == 0 && c12 == '?' &&
+ c13 == 0 && c14 == 0 && c15 == 0 && c16 == 'x') {
+ return "application/xml";
+ }
+ }
+
+ if (c1 == 0xff && c2 == 0xfe && c3 == 0x00 && c4 == 0x00) {
+ if (c5 == '<' && c6 == 0 && c7 == 0 && c8 == 0 &&
+ c9 == '?' && c10 == 0 && c11 == 0 && c12 == 0 &&
+ c13 == 'x' && c14 == 0 && c15 == 0 && c16 == 0) {
+ return "application/xml";
+ }
+ }
+
+ if (c1 == 'G' && c2 == 'I' && c3 == 'F' && c4 == '8') {
+ return "image/gif";
+ }
+
+ if (c1 == '#' && c2 == 'd' && c3 == 'e' && c4 == 'f') {
+ return "image/x-bitmap";
+ }
+
+ if (c1 == '!' && c2 == ' ' && c3 == 'X' && c4 == 'P' &&
+ c5 == 'M' && c6 == '2') {
+ return "image/x-pixmap";
+ }
+
+ if (c1 == 137 && c2 == 80 && c3 == 78 &&
+ c4 == 71 && c5 == 13 && c6 == 10 &&
+ c7 == 26 && c8 == 10) {
+ return "image/png";
+ }
+
+ if (c1 == 0xFF && c2 == 0xD8 && c3 == 0xFF) {
+ if (c4 == 0xE0 || c4 == 0xEE) {
+ return "image/jpeg";
+ }
+
+ /**
+ * File format used by digital cameras to store images.
+ * Exif Format can be read by any application supporting
+ * JPEG. Exif Spec can be found at:
+ * http://www.pima.net/standards/it10/PIMA15740/Exif_2-1.PDF
+ */
+ if ((c4 == 0xE1) &&
+ (c7 == 'E' && c8 == 'x' && c9 == 'i' && c10 =='f' &&
+ c11 == 0)) {
+ return "image/jpeg";
+ }
+ }
+
+ if (c1 == 0xD0 && c2 == 0xCF && c3 == 0x11 && c4 == 0xE0 &&
+ c5 == 0xA1 && c6 == 0xB1 && c7 == 0x1A && c8 == 0xE1) {
+
+ /* Above is signature of Microsoft Structured Storage.
+ * Below this, could have tests for various SS entities.
+ * For now, just test for FlashPix.
+ */
+ if (checkfpx(is)) {
+ return "image/vnd.fpx";
+ }
+ }
+
+ if (c1 == 0x2E && c2 == 0x73 && c3 == 0x6E && c4 == 0x64) {
+ return "audio/basic"; // .au format, big endian
+ }
+
+ if (c1 == 0x64 && c2 == 0x6E && c3 == 0x73 && c4 == 0x2E) {
+ return "audio/basic"; // .au format, little endian
+ }
+
+ if (c1 == 'R' && c2 == 'I' && c3 == 'F' && c4 == 'F') {
+ /* I don't know if this is official but evidence
+ * suggests that .wav files start with "RIFF" - brown
+ */
+ return "audio/x-wav";
+ }
+ return null;
+ }
+
+ /**
+ * Check for FlashPix image data in InputStream is. Return true if
+ * the stream has FlashPix data, false otherwise. Before calling this
+ * method, the stream should have already been checked to be sure it
+ * contains Microsoft Structured Storage data.
+ */
+ static private boolean checkfpx(InputStream is) throws IOException {
+
+ /* Test for FlashPix image data in Microsoft Structured Storage format.
+ * In general, should do this with calls to an SS implementation.
+ * Lacking that, need to dig via offsets to get to the FlashPix
+ * ClassID. Details:
+ *
+ * Offset to Fpx ClsID from beginning of stream should be:
+ *
+ * FpxClsidOffset = rootEntryOffset + clsidOffset
+ *
+ * where: clsidOffset = 0x50.
+ * rootEntryOffset = headerSize + sectorSize*sectDirStart
+ * + 128*rootEntryDirectory
+ *
+ * where: headerSize = 0x200 (always)
+ * sectorSize = 2 raised to power of uSectorShift,
+ * which is found in the header at
+ * offset 0x1E.
+ * sectDirStart = found in the header at offset 0x30.
+ * rootEntryDirectory = in general, should search for
+ * directory labelled as root.
+ * We will assume value of 0 (i.e.,
+ * rootEntry is in first directory)
+ */
+
+ // Mark the stream so we can reset it. 0x100 is enough for the first
+ // few reads, but the mark will have to be reset and set again once
+ // the offset to the root directory entry is computed. That offset
+ // can be very large and isn't know until the stream has been read from
+ is.mark(0x100);
+
+ // Get the byte ordering located at 0x1E. 0xFE is Intel,
+ // 0xFF is other
+ long toSkip = (long)0x1C;
+ long posn;
+
+ if ((posn = skipForward(is, toSkip)) < toSkip) {
+ is.reset();
+ return false;
+ }
+
+ int c[] = new int[16];
+ if (readBytes(c, 2, is) < 0) {
+ is.reset();
+ return false;
+ }
+
+ int byteOrder = c[0];
+
+ posn+=2;
+ int uSectorShift;
+ if (readBytes(c, 2, is) < 0) {
+ is.reset();
+ return false;
+ }
+
+ if(byteOrder == 0xFE) {
+ uSectorShift = c[0];
+ uSectorShift += c[1] << 8;
+ }
+ else {
+ uSectorShift = c[0] << 8;
+ uSectorShift += c[1];
+ }
+
+ posn += 2;
+ toSkip = (long)0x30 - posn;
+ long skipped = 0;
+ if ((skipped = skipForward(is, toSkip)) < toSkip) {
+ is.reset();
+ return false;
+ }
+ posn += skipped;
+
+ if (readBytes(c, 4, is) < 0) {
+ is.reset();
+ return false;
+ }
+
+ int sectDirStart;
+ if(byteOrder == 0xFE) {
+ sectDirStart = c[0];
+ sectDirStart += c[1] << 8;
+ sectDirStart += c[2] << 16;
+ sectDirStart += c[3] << 24;
+ } else {
+ sectDirStart = c[0] << 24;
+ sectDirStart += c[1] << 16;
+ sectDirStart += c[2] << 8;
+ sectDirStart += c[3];
+ }
+ posn += 4;
+ is.reset(); // Reset back to the beginning
+
+ toSkip = 0x200L + (long)(1<<uSectorShift)*sectDirStart + 0x50L;
+
+ // Sanity check!
+ if (toSkip < 0) {
+ return false;
+ }
+
+ /*
+ * How far can we skip? Is there any performance problem here?
+ * This skip can be fairly long, at least 0x4c650 in at least
+ * one case. Have to assume that the skip will fit in an int.
+ * Leave room to read whole root dir
+ */
+ is.mark((int)toSkip+0x30);
+
+ if ((skipForward(is, toSkip)) < toSkip) {
+ is.reset();
+ return false;
+ }
+
+ /* should be at beginning of ClassID, which is as follows
+ * (in Intel byte order):
+ * 00 67 61 56 54 C1 CE 11 85 53 00 AA 00 A1 F9 5B
+ *
+ * This is stored from Windows as long,short,short,char[8]
+ * so for byte order changes, the order only changes for
+ * the first 8 bytes in the ClassID.
+ *
+ * Test against this, ignoring second byte (Intel) since
+ * this could change depending on part of Fpx file we have.
+ */
+
+ if (readBytes(c, 16, is) < 0) {
+ is.reset();
+ return false;
+ }
+
+ // intel byte order
+ if (byteOrder == 0xFE &&
+ c[0] == 0x00 && c[2] == 0x61 && c[3] == 0x56 &&
+ c[4] == 0x54 && c[5] == 0xC1 && c[6] == 0xCE &&
+ c[7] == 0x11 && c[8] == 0x85 && c[9] == 0x53 &&
+ c[10]== 0x00 && c[11]== 0xAA && c[12]== 0x00 &&
+ c[13]== 0xA1 && c[14]== 0xF9 && c[15]== 0x5B) {
+ is.reset();
+ return true;
+ }
+
+ // non-intel byte order
+ else if (c[3] == 0x00 && c[1] == 0x61 && c[0] == 0x56 &&
+ c[5] == 0x54 && c[4] == 0xC1 && c[7] == 0xCE &&
+ c[6] == 0x11 && c[8] == 0x85 && c[9] == 0x53 &&
+ c[10]== 0x00 && c[11]== 0xAA && c[12]== 0x00 &&
+ c[13]== 0xA1 && c[14]== 0xF9 && c[15]== 0x5B) {
+ is.reset();
+ return true;
+ }
+ is.reset();
+ return false;
+ }
+
+ /**
+ * Tries to read the specified number of bytes from the stream
+ * Returns -1, If EOF is reached before len bytes are read, returns 0
+ * otherwise
+ */
+ static private int readBytes(int c[], int len, InputStream is)
+ throws IOException {
+
+ byte buf[] = new byte[len];
+ if (is.read(buf, 0, len) < len) {
+ return -1;
+ }
+
+ // fill the passed in int array
+ for (int i = 0; i < len; i++) {
+ c[i] = buf[i] & 0xff;
+ }
+ return 0;
+ }
+
+
+ /**
+ * Skips through the specified number of bytes from the stream
+ * until either EOF is reached, or the specified
+ * number of bytes have been skipped
+ */
+ static private long skipForward(InputStream is, long toSkip)
+ throws IOException {
+
+ long eachSkip = 0;
+ long skipped = 0;
+
+ while (skipped != toSkip) {
+ eachSkip = is.skip(toSkip - skipped);
+
+ // check if EOF is reached
+ if (eachSkip <= 0) {
+ if (is.read() == -1) {
+ return skipped ;
+ } else {
+ skipped++;
+ }
+ }
+ skipped += eachSkip;
+ }
+ return skipped;
+ }
+
+}
+
+
+class UnknownContentHandler extends ContentHandler {
+ static final ContentHandler INSTANCE = new UnknownContentHandler();
+
+ public Object getContent(URLConnection uc) throws IOException {
+ return uc.getInputStream();
+ }
+}
diff --git a/java/net/URLDecoder.java b/java/net/URLDecoder.java
new file mode 100644
index 0000000..8b14eb8
--- /dev/null
+++ b/java/net/URLDecoder.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.*;
+
+/**
+ * Utility class for HTML form decoding. This class contains static methods
+ * for decoding a String from the <CODE>application/x-www-form-urlencoded</CODE>
+ * MIME format.
+ * <p>
+ * The conversion process is the reverse of that used by the URLEncoder class. It is assumed
+ * that all characters in the encoded string are one of the following:
+ * "{@code a}" through "{@code z}",
+ * "{@code A}" through "{@code Z}",
+ * "{@code 0}" through "{@code 9}", and
+ * "{@code -}", "{@code _}",
+ * "{@code .}", and "{@code *}". The
+ * character "{@code %}" is allowed but is interpreted
+ * as the start of a special escaped sequence.
+ * <p>
+ * The following rules are applied in the conversion:
+ *
+ * <ul>
+ * <li>The alphanumeric characters "{@code a}" through
+ * "{@code z}", "{@code A}" through
+ * "{@code Z}" and "{@code 0}"
+ * through "{@code 9}" remain the same.
+ * <li>The special characters "{@code .}",
+ * "{@code -}", "{@code *}", and
+ * "{@code _}" remain the same.
+ * <li>The plus sign "{@code +}" is converted into a
+ * space character " " .
+ * <li>A sequence of the form "<i>{@code %xy}</i>" will be
+ * treated as representing a byte where <i>xy</i> is the two-digit
+ * hexadecimal representation of the 8 bits. Then, all substrings
+ * that contain one or more of these byte sequences consecutively
+ * will be replaced by the character(s) whose encoding would result
+ * in those consecutive bytes.
+ * The encoding scheme used to decode these characters may be specified,
+ * or if unspecified, the default encoding of the platform will be used.
+ * </ul>
+ * <p>
+ * There are two possible ways in which this decoder could deal with
+ * illegal strings. It could either leave illegal characters alone or
+ * it could throw an {@link java.lang.IllegalArgumentException}.
+ * Which approach the decoder takes is left to the
+ * implementation.
+ *
+ * @author Mark Chamness
+ * @author Michael McCloskey
+ * @since 1.2
+ */
+
+public class URLDecoder {
+
+ // The platform default encoding
+ static String dfltEncName = URLEncoder.dfltEncName;
+
+ /**
+ * Decodes a {@code x-www-form-urlencoded} string.
+ * The platform's default encoding is used to determine what characters
+ * are represented by any consecutive sequences of the form
+ * "<i>{@code %xy}</i>".
+ * @param s the {@code String} to decode
+ * @deprecated The resulting string may vary depending on the platform's
+ * default encoding. Instead, use the decode(String,String) method
+ * to specify the encoding.
+ * @return the newly decoded {@code String}
+ */
+ @Deprecated
+ public static String decode(String s) {
+
+ String str = null;
+
+ try {
+ str = decode(s, dfltEncName);
+ } catch (UnsupportedEncodingException e) {
+ // The system should always have the platform default
+ }
+
+ return str;
+ }
+
+ /**
+ * Decodes a {@code application/x-www-form-urlencoded} string using a specific
+ * encoding scheme.
+ * The supplied encoding is used to determine
+ * what characters are represented by any consecutive sequences of the
+ * form "<i>{@code %xy}</i>".
+ * <p>
+ * <em><strong>Note:</strong> The <a href=
+ * "http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars">
+ * World Wide Web Consortium Recommendation</a> states that
+ * UTF-8 should be used. Not doing so may introduce
+ * incompatibilities.</em>
+ *
+ * @param s the {@code String} to decode
+ * @param enc The name of a supported
+ * <a href="../lang/package-summary.html#charenc">character
+ * encoding</a>.
+ * @return the newly decoded {@code String}
+ * @exception UnsupportedEncodingException
+ * If character encoding needs to be consulted, but
+ * named character encoding is not supported
+ * @see URLEncoder#encode(java.lang.String, java.lang.String)
+ * @since 1.4
+ */
+ public static String decode(String s, String enc)
+ throws UnsupportedEncodingException{
+
+ boolean needToChange = false;
+ int numChars = s.length();
+ StringBuffer sb = new StringBuffer(numChars > 500 ? numChars / 2 : numChars);
+ int i = 0;
+
+ if (enc.length() == 0) {
+ throw new UnsupportedEncodingException ("URLDecoder: empty string enc parameter");
+ }
+
+ char c;
+ byte[] bytes = null;
+ while (i < numChars) {
+ c = s.charAt(i);
+ switch (c) {
+ case '+':
+ sb.append(' ');
+ i++;
+ needToChange = true;
+ break;
+ case '%':
+ /*
+ * Starting with this instance of %, process all
+ * consecutive substrings of the form %xy. Each
+ * substring %xy will yield a byte. Convert all
+ * consecutive bytes obtained this way to whatever
+ * character(s) they represent in the provided
+ * encoding.
+ */
+
+ try {
+
+ // (numChars-i)/3 is an upper bound for the number
+ // of remaining bytes
+ if (bytes == null)
+ bytes = new byte[(numChars-i)/3];
+ int pos = 0;
+
+ while ( ((i+2) < numChars) &&
+ (c=='%')) {
+ // BEGIN Android-changed: App compat. Forbid non-hex chars after '%'.
+ if (!isValidHexChar(s.charAt(i+1)) || !isValidHexChar(s.charAt(i+2))) {
+ throw new IllegalArgumentException("URLDecoder: Illegal hex characters in escape (%) pattern : "
+ + s.substring(i, i + 3));
+ }
+ // END Android-changed: App compat. Forbid non-hex chars after '%'.
+ int v = Integer.parseInt(s.substring(i+1,i+3),16);
+ if (v < 0)
+ // Android-changed: Improve error message by printing the string value.
+ throw new IllegalArgumentException("URLDecoder: Illegal hex characters in escape (%) pattern - negative value : "
+ + s.substring(i, i + 3));
+ bytes[pos++] = (byte) v;
+ i+= 3;
+ if (i < numChars)
+ c = s.charAt(i);
+ }
+
+ // A trailing, incomplete byte encoding such as
+ // "%x" will cause an exception to be thrown
+
+ if ((i < numChars) && (c=='%'))
+ throw new IllegalArgumentException(
+ "URLDecoder: Incomplete trailing escape (%) pattern");
+
+ sb.append(new String(bytes, 0, pos, enc));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(
+ "URLDecoder: Illegal hex characters in escape (%) pattern - "
+ + e.getMessage());
+ }
+ needToChange = true;
+ break;
+ default:
+ sb.append(c);
+ i++;
+ break;
+ }
+ }
+
+ return (needToChange? sb.toString() : s);
+ }
+
+ // BEGIN Android-added: App compat. Forbid non-hex chars after '%'.
+ private static boolean isValidHexChar(char c) {
+ return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
+ }
+ // END Android-added: App compat. Forbid non-hex chars after '%'.
+}
diff --git a/java/net/URLEncoder.java b/java/net/URLEncoder.java
new file mode 100644
index 0000000..86377b7
--- /dev/null
+++ b/java/net/URLEncoder.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.ByteArrayOutputStream;
+import java.io.BufferedWriter;
+import java.io.OutputStreamWriter;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.io.CharArrayWriter;
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException ;
+import java.util.BitSet;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import sun.security.action.GetBooleanAction;
+import sun.security.action.GetPropertyAction;
+
+/**
+ * Utility class for HTML form encoding. This class contains static methods
+ * for converting a String to the <CODE>application/x-www-form-urlencoded</CODE> MIME
+ * format. For more information about HTML form encoding, consult the HTML
+ * <A HREF="http://www.w3.org/TR/html4/">specification</A>.
+ *
+ * <p>
+ * When encoding a String, the following rules apply:
+ *
+ * <ul>
+ * <li>The alphanumeric characters "{@code a}" through
+ * "{@code z}", "{@code A}" through
+ * "{@code Z}" and "{@code 0}"
+ * through "{@code 9}" remain the same.
+ * <li>The special characters "{@code .}",
+ * "{@code -}", "{@code *}", and
+ * "{@code _}" remain the same.
+ * <li>The space character " " is
+ * converted into a plus sign "{@code +}".
+ * <li>All other characters are unsafe and are first converted into
+ * one or more bytes using some encoding scheme. Then each byte is
+ * represented by the 3-character string
+ * "<i>{@code %xy}</i>", where <i>xy</i> is the
+ * two-digit hexadecimal representation of the byte.
+ * The recommended encoding scheme to use is UTF-8. However,
+ * for compatibility reasons, if an encoding is not specified,
+ * then the default encoding of the platform is used.
+ * </ul>
+ *
+ * <p>
+ * For example using UTF-8 as the encoding scheme the string "The
+ * string ü@foo-bar" would get converted to
+ * "The+string+%C3%BC%40foo-bar" because in UTF-8 the character
+ * ü is encoded as two bytes C3 (hex) and BC (hex), and the
+ * character @ is encoded as one byte 40 (hex).
+ *
+ * @author Herb Jellinek
+ * @since JDK1.0
+ */
+public class URLEncoder {
+ static BitSet dontNeedEncoding;
+ static final int caseDiff = ('a' - 'A');
+ static String dfltEncName = null;
+
+ static {
+
+ /* The list of characters that are not encoded has been
+ * determined as follows:
+ *
+ * RFC 2396 states:
+ * -----
+ * Data characters that are allowed in a URI but do not have a
+ * reserved purpose are called unreserved. These include upper
+ * and lower case letters, decimal digits, and a limited set of
+ * punctuation marks and symbols.
+ *
+ * unreserved = alphanum | mark
+ *
+ * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+ *
+ * Unreserved characters can be escaped without changing the
+ * semantics of the URI, but this should not be done unless the
+ * URI is being used in a context that does not allow the
+ * unescaped character to appear.
+ * -----
+ *
+ * It appears that both Netscape and Internet Explorer escape
+ * all special characters from this list with the exception
+ * of "-", "_", ".", "*". While it is not clear why they are
+ * escaping the other characters, perhaps it is safest to
+ * assume that there might be contexts in which the others
+ * are unsafe if not escaped. Therefore, we will use the same
+ * list. It is also noteworthy that this is consistent with
+ * O'Reilly's "HTML: The Definitive Guide" (page 164).
+ *
+ * As a last note, Intenet Explorer does not encode the "@"
+ * character which is clearly not unreserved according to the
+ * RFC. We are being consistent with the RFC in this matter,
+ * as is Netscape.
+ *
+ */
+
+ dontNeedEncoding = new BitSet(256);
+ int i;
+ for (i = 'a'; i <= 'z'; i++) {
+ dontNeedEncoding.set(i);
+ }
+ for (i = 'A'; i <= 'Z'; i++) {
+ dontNeedEncoding.set(i);
+ }
+ for (i = '0'; i <= '9'; i++) {
+ dontNeedEncoding.set(i);
+ }
+ dontNeedEncoding.set(' '); /* encoding a space to a + is done
+ * in the encode() method */
+ dontNeedEncoding.set('-');
+ dontNeedEncoding.set('_');
+ dontNeedEncoding.set('.');
+ dontNeedEncoding.set('*');
+
+ dfltEncName = AccessController.doPrivileged(
+ new GetPropertyAction("file.encoding")
+ );
+ }
+
+ /**
+ * You can't call the constructor.
+ */
+ private URLEncoder() { }
+
+ /**
+ * Translates a string into {@code x-www-form-urlencoded}
+ * format. This method uses the platform's default encoding
+ * as the encoding scheme to obtain the bytes for unsafe characters.
+ *
+ * @param s {@code String} to be translated.
+ * @deprecated The resulting string may vary depending on the platform's
+ * default encoding. Instead, use the encode(String,String)
+ * method to specify the encoding.
+ * @return the translated {@code String}.
+ */
+ @Deprecated
+ public static String encode(String s) {
+
+ String str = null;
+
+ try {
+ str = encode(s, dfltEncName);
+ } catch (UnsupportedEncodingException e) {
+ // The system should always have the platform default
+ }
+
+ return str;
+ }
+
+ /**
+ * Translates a string into {@code application/x-www-form-urlencoded}
+ * format using a specific encoding scheme. This method uses the
+ * supplied encoding scheme to obtain the bytes for unsafe
+ * characters.
+ * <p>
+ * <em><strong>Note:</strong> The <a href=
+ * "http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars">
+ * World Wide Web Consortium Recommendation</a> states that
+ * UTF-8 should be used. Not doing so may introduce
+ * incompatibilities.</em>
+ *
+ * @param s {@code String} to be translated.
+ * @param enc The name of a supported
+ * <a href="../lang/package-summary.html#charenc">character
+ * encoding</a>.
+ * @return the translated {@code String}.
+ * @exception UnsupportedEncodingException
+ * If the named encoding is not supported
+ * @see URLDecoder#decode(java.lang.String, java.lang.String)
+ * @since 1.4
+ */
+ public static String encode(String s, String enc)
+ throws UnsupportedEncodingException {
+
+ boolean needToChange = false;
+ StringBuffer out = new StringBuffer(s.length());
+ Charset charset;
+ CharArrayWriter charArrayWriter = new CharArrayWriter();
+
+ if (enc == null)
+ throw new NullPointerException("charsetName");
+
+ try {
+ charset = Charset.forName(enc);
+ } catch (IllegalCharsetNameException e) {
+ throw new UnsupportedEncodingException(enc);
+ } catch (UnsupportedCharsetException e) {
+ throw new UnsupportedEncodingException(enc);
+ }
+
+ for (int i = 0; i < s.length();) {
+ int c = (int) s.charAt(i);
+ //System.out.println("Examining character: " + c);
+ if (dontNeedEncoding.get(c)) {
+ if (c == ' ') {
+ c = '+';
+ needToChange = true;
+ }
+ //System.out.println("Storing: " + c);
+ out.append((char)c);
+ i++;
+ } else {
+ // convert to external encoding before hex conversion
+ do {
+ charArrayWriter.write(c);
+ /*
+ * If this character represents the start of a Unicode
+ * surrogate pair, then pass in two characters. It's not
+ * clear what should be done if a bytes reserved in the
+ * surrogate pairs range occurs outside of a legal
+ * surrogate pair. For now, just treat it as if it were
+ * any other character.
+ */
+ if (c >= 0xD800 && c <= 0xDBFF) {
+ /*
+ System.out.println(Integer.toHexString(c)
+ + " is high surrogate");
+ */
+ if ( (i+1) < s.length()) {
+ int d = (int) s.charAt(i+1);
+ /*
+ System.out.println("\tExamining "
+ + Integer.toHexString(d));
+ */
+ if (d >= 0xDC00 && d <= 0xDFFF) {
+ /*
+ System.out.println("\t"
+ + Integer.toHexString(d)
+ + " is low surrogate");
+ */
+ charArrayWriter.write(d);
+ i++;
+ }
+ }
+ }
+ i++;
+ } while (i < s.length() && !dontNeedEncoding.get((c = (int) s.charAt(i))));
+
+ charArrayWriter.flush();
+ String str = new String(charArrayWriter.toCharArray());
+ byte[] ba = str.getBytes(charset);
+ for (int j = 0; j < ba.length; j++) {
+ out.append('%');
+ char ch = Character.forDigit((ba[j] >> 4) & 0xF, 16);
+ // converting to use uppercase letter as part of
+ // the hex value if ch is a letter.
+ if (Character.isLetter(ch)) {
+ ch -= caseDiff;
+ }
+ out.append(ch);
+ ch = Character.forDigit(ba[j] & 0xF, 16);
+ if (Character.isLetter(ch)) {
+ ch -= caseDiff;
+ }
+ out.append(ch);
+ }
+ charArrayWriter.reset();
+ needToChange = true;
+ }
+ }
+
+ return (needToChange? out.toString() : s);
+ }
+}
diff --git a/java/net/URLStreamHandler.java b/java/net/URLStreamHandler.java
new file mode 100644
index 0000000..dffc6d5
--- /dev/null
+++ b/java/net/URLStreamHandler.java
@@ -0,0 +1,626 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+import java.util.Objects;
+
+import sun.net.util.IPAddressUtil;
+
+/**
+ * The abstract class {@code URLStreamHandler} is the common
+ * superclass for all stream protocol handlers. A stream protocol
+ * handler knows how to make a connection for a particular protocol
+ * type, such as {@code http} or {@code https}.
+ * <p>
+ * In most cases, an instance of a {@code URLStreamHandler}
+ * subclass is not created directly by an application. Rather, the
+ * first time a protocol name is encountered when constructing a
+ * {@code URL}, the appropriate stream protocol handler is
+ * automatically loaded.
+ *
+ * @author James Gosling
+ * @see java.net.URL#URL(java.lang.String, java.lang.String, int, java.lang.String)
+ * @since JDK1.0
+ */
+public abstract class URLStreamHandler {
+ /**
+ * Opens a connection to the object referenced by the
+ * {@code URL} argument.
+ * This method should be overridden by a subclass.
+ *
+ * <p>If for the handler's protocol (such as HTTP or JAR), there
+ * exists a public, specialized URLConnection subclass belonging
+ * to one of the following packages or one of their subpackages:
+ * java.lang, java.io, java.util, java.net, the connection
+ * returned will be of that subclass. For example, for HTTP an
+ * HttpURLConnection will be returned, and for JAR a
+ * JarURLConnection will be returned.
+ *
+ * @param u the URL that this connects to.
+ * @return a {@code URLConnection} object for the {@code URL}.
+ * @exception IOException if an I/O error occurs while opening the
+ * connection.
+ */
+ abstract protected URLConnection openConnection(URL u) throws IOException;
+
+ /**
+ * Same as openConnection(URL), except that the connection will be
+ * made through the specified proxy; Protocol handlers that do not
+ * support proxying will ignore the proxy parameter and make a
+ * normal connection.
+ *
+ * Calling this method preempts the system's default ProxySelector
+ * settings.
+ *
+ * @param u the URL that this connects to.
+ * @param p the proxy through which the connection will be made.
+ * If direct connection is desired, Proxy.NO_PROXY
+ * should be specified.
+ * @return a {@code URLConnection} object for the {@code URL}.
+ * @exception IOException if an I/O error occurs while opening the
+ * connection.
+ * @exception IllegalArgumentException if either u or p is null,
+ * or p has the wrong type.
+ * @exception UnsupportedOperationException if the subclass that
+ * implements the protocol doesn't support this method.
+ * @since 1.5
+ */
+ protected URLConnection openConnection(URL u, Proxy p) throws IOException {
+ throw new UnsupportedOperationException("Method not implemented.");
+ }
+
+ /**
+ * Parses the string representation of a {@code URL} into a
+ * {@code URL} object.
+ * <p>
+ * If there is any inherited context, then it has already been
+ * copied into the {@code URL} argument.
+ * <p>
+ * The {@code parseURL} method of {@code URLStreamHandler}
+ * parses the string representation as if it were an
+ * {@code http} specification. Most URL protocol families have a
+ * similar parsing. A stream protocol handler for a protocol that has
+ * a different syntax must override this routine.
+ *
+ * @param u the {@code URL} to receive the result of parsing
+ * the spec.
+ * @param spec the {@code String} representing the URL that
+ * must be parsed.
+ * @param start the character index at which to begin parsing. This is
+ * just past the '{@code :}' (if there is one) that
+ * specifies the determination of the protocol name.
+ * @param limit the character position to stop parsing at. This is the
+ * end of the string or the position of the
+ * "{@code #}" character, if present. All information
+ * after the sharp sign indicates an anchor.
+ */
+ protected void parseURL(URL u, String spec, int start, int limit) {
+ // These fields may receive context content if this was relative URL
+ String protocol = u.getProtocol();
+ String authority = u.getAuthority();
+ String userInfo = u.getUserInfo();
+ String host = u.getHost();
+ int port = u.getPort();
+ String path = u.getPath();
+ String query = u.getQuery();
+
+ // This field has already been parsed
+ String ref = u.getRef();
+
+ boolean isRelPath = false;
+ boolean queryOnly = false;
+ // BEGIN Android-changed: App compat
+ boolean querySet = false;
+ // END Android-changed: App compat
+
+// FIX: should not assume query if opaque
+ // Strip off the query part
+ if (start < limit) {
+ int queryStart = spec.indexOf('?');
+ queryOnly = queryStart == start;
+ if ((queryStart != -1) && (queryStart < limit)) {
+ query = spec.substring(queryStart+1, limit);
+ if (limit > queryStart)
+ limit = queryStart;
+ spec = spec.substring(0, queryStart);
+ // BEGIN Android-changed: App compat
+ querySet = true;
+ // END Android-changed: App compat
+ }
+ }
+
+ int i = 0;
+ // Parse the authority part if any
+ // BEGIN Android-changed: App compat
+ // boolean isUNCName = (start <= limit - 4) &&
+ // (spec.charAt(start) == '/') &&
+ // (spec.charAt(start + 1) == '/') &&
+ // (spec.charAt(start + 2) == '/') &&
+ // (spec.charAt(start + 3) == '/');
+ boolean isUNCName = false;
+ // END Android-changed: App compat
+ if (!isUNCName && (start <= limit - 2) && (spec.charAt(start) == '/') &&
+ (spec.charAt(start + 1) == '/')) {
+ start += 2;
+ // BEGIN Android-changed: Check for all hostname termination chars. http://b/110955991
+ /*
+ i = spec.indexOf('/', start);
+ if (i < 0 || i > limit) {
+ i = spec.indexOf('?', start);
+ if (i < 0 || i > limit)
+ i = limit;
+ }
+ */
+ LOOP: for (i = start; i < limit; i++) {
+ switch (spec.charAt(i)) {
+ case '/': // Start of path
+ case '\\': // Start of path - see https://url.spec.whatwg.org/#host-state
+ case '?': // Start of query
+ case '#': // Start of fragment
+ break LOOP;
+ }
+ }
+ // END Android-changed: Check for all hostname termination chars. http://b/110955991
+
+ host = authority = spec.substring(start, i);
+
+ int ind = authority.indexOf('@');
+ if (ind != -1) {
+ if (ind != authority.lastIndexOf('@')) {
+ // more than one '@' in authority. This is not server based
+ userInfo = null;
+ host = null;
+ } else {
+ userInfo = authority.substring(0, ind);
+ host = authority.substring(ind+1);
+ }
+ } else {
+ userInfo = null;
+ }
+ if (host != null) {
+ // If the host is surrounded by [ and ] then its an IPv6
+ // literal address as specified in RFC2732
+ if (host.length()>0 && (host.charAt(0) == '[')) {
+ if ((ind = host.indexOf(']')) > 2) {
+
+ String nhost = host ;
+ host = nhost.substring(0,ind+1);
+ if (!IPAddressUtil.
+ isIPv6LiteralAddress(host.substring(1, ind))) {
+ throw new IllegalArgumentException(
+ "Invalid host: "+ host);
+ }
+
+ port = -1 ;
+ if (nhost.length() > ind+1) {
+ if (nhost.charAt(ind+1) == ':') {
+ ++ind ;
+ // port can be null according to RFC2396
+ if (nhost.length() > (ind + 1)) {
+ port = Integer.parseInt(nhost.substring(ind+1));
+ }
+ } else {
+ throw new IllegalArgumentException(
+ "Invalid authority field: " + authority);
+ }
+ }
+ } else {
+ throw new IllegalArgumentException(
+ "Invalid authority field: " + authority);
+ }
+ } else {
+ ind = host.indexOf(':');
+ port = -1;
+ if (ind >= 0) {
+ // port can be null according to RFC2396
+ if (host.length() > (ind + 1)) {
+ // BEGIN Android-changed: App compat
+ // port = Integer.parseInt(host.substring(ind + 1));
+ char firstPortChar = host.charAt(ind+1);
+ if (firstPortChar >= '0' && firstPortChar <= '9') {
+ port = Integer.parseInt(host.substring(ind + 1));
+ } else {
+ throw new IllegalArgumentException("invalid port: " +
+ host.substring(ind + 1));
+ }
+ // END Android-changed: App compat
+ }
+ host = host.substring(0, ind);
+ }
+ }
+ } else {
+ host = "";
+ }
+ if (port < -1)
+ throw new IllegalArgumentException("Invalid port number :" +
+ port);
+ start = i;
+
+ // If the authority is defined then the path is defined by the
+ // spec only; See RFC 2396 Section 5.2.4.
+ // BEGIN Android-changed: App compat
+ // if (authority != null && authority.length() > 0)
+ // path = "";
+ path = null;
+ if (!querySet) {
+ query = null;
+ }
+ // END Android-changed: App compat
+ }
+
+ if (host == null) {
+ host = "";
+ }
+
+ // Parse the file path if any
+ if (start < limit) {
+ // Android-changed: Check for all hostname termination chars. http://b/110955991
+ // if (spec.charAt(start) == '/') {
+ if (spec.charAt(start) == '/' || spec.charAt(start) == '\\') {
+ path = spec.substring(start, limit);
+ } else if (path != null && path.length() > 0) {
+ isRelPath = true;
+ int ind = path.lastIndexOf('/');
+ String seperator = "";
+ if (ind == -1 && authority != null)
+ seperator = "/";
+ path = path.substring(0, ind + 1) + seperator +
+ spec.substring(start, limit);
+
+ } else {
+ String seperator = (authority != null) ? "/" : "";
+ path = seperator + spec.substring(start, limit);
+ }
+ }
+ // BEGIN Android-changed: App compat
+ //else if (queryOnly && path != null) {
+ // int ind = path.lastIndexOf('/');
+ // if (ind < 0)
+ // ind = 0;
+ // path = path.substring(0, ind) + "/";
+ //}
+ // END Android-changed: App compat
+ if (path == null)
+ path = "";
+
+ // BEGIN Android-changed
+ //if (isRelPath) {
+ if (true) {
+ // END Android-changed
+ // Remove embedded /./
+ while ((i = path.indexOf("/./")) >= 0) {
+ path = path.substring(0, i) + path.substring(i + 2);
+ }
+ // Remove embedded /../ if possible
+ i = 0;
+ while ((i = path.indexOf("/../", i)) >= 0) {
+ // BEGIN Android-changed: App compat
+ /*
+ * Trailing /../
+ */
+ if (i == 0) {
+ path = path.substring(i + 3);
+ i = 0;
+ // END Android-changed: App compat
+ /*
+ * A "/../" will cancel the previous segment and itself,
+ * unless that segment is a "/../" itself
+ * i.e. "/a/b/../c" becomes "/a/c"
+ * but "/../../a" should stay unchanged
+ */
+ // Android-changed: App compat
+ // if (i > 0 && (limit = path.lastIndexOf('/', i - 1)) >= 0 &&
+ } else if (i > 0 && (limit = path.lastIndexOf('/', i - 1)) >= 0 &&
+ (path.indexOf("/../", limit) != 0)) {
+ path = path.substring(0, limit) + path.substring(i + 3);
+ i = 0;
+ } else {
+ i = i + 3;
+ }
+ }
+ // Remove trailing .. if possible
+ while (path.endsWith("/..")) {
+ i = path.indexOf("/..");
+ if ((limit = path.lastIndexOf('/', i - 1)) >= 0) {
+ path = path.substring(0, limit+1);
+ } else {
+ break;
+ }
+ }
+ // Remove starting .
+ if (path.startsWith("./") && path.length() > 2)
+ path = path.substring(2);
+
+ // Remove trailing .
+ if (path.endsWith("/."))
+ path = path.substring(0, path.length() -1);
+
+ // Android-changed: App compat: Remove trailing ?
+ if (path.endsWith("?"))
+ path = path.substring(0, path.length() -1);
+ }
+
+ setURL(u, protocol, host, port, authority, userInfo, path, query, ref);
+ }
+
+ /**
+ * Returns the default port for a URL parsed by this handler. This method
+ * is meant to be overidden by handlers with default port numbers.
+ * @return the default port for a {@code URL} parsed by this handler.
+ * @since 1.3
+ */
+ protected int getDefaultPort() {
+ return -1;
+ }
+
+ /**
+ * Provides the default equals calculation. May be overidden by handlers
+ * for other protocols that have different requirements for equals().
+ * This method requires that none of its arguments is null. This is
+ * guaranteed by the fact that it is only called by java.net.URL class.
+ * @param u1 a URL object
+ * @param u2 a URL object
+ * @return {@code true} if the two urls are
+ * considered equal, ie. they refer to the same
+ * fragment in the same file.
+ * @since 1.3
+ */
+ protected boolean equals(URL u1, URL u2) {
+ // Android-changed: Avoid network I/O
+ return Objects.equals(u1.getRef(), u2.getRef()) &&
+ Objects.equals(u1.getQuery(), u2.getQuery()) &&
+ // sameFile compares the protocol, file, port & host components of
+ // the URLs.
+ sameFile(u1, u2);
+ }
+
+ /**
+ * Provides the default hash calculation. May be overidden by handlers for
+ * other protocols that have different requirements for hashCode
+ * calculation.
+ * @param u a URL object
+ * @return an {@code int} suitable for hash table indexing
+ * @since 1.3
+ */
+ protected int hashCode(URL u) {
+ // Android-changed: Avoid network I/O
+ // Hash on the same set of fields that we compare in equals().
+ return Objects.hash(
+ u.getRef(),
+ u.getQuery(),
+ u.getProtocol(),
+ u.getFile(),
+ u.getHost(),
+ u.getPort());
+ }
+
+ /**
+ * Compare two urls to see whether they refer to the same file,
+ * i.e., having the same protocol, host, port, and path.
+ * This method requires that none of its arguments is null. This is
+ * guaranteed by the fact that it is only called indirectly
+ * by java.net.URL class.
+ * @param u1 a URL object
+ * @param u2 a URL object
+ * @return true if u1 and u2 refer to the same file
+ * @since 1.3
+ */
+ protected boolean sameFile(URL u1, URL u2) {
+ // Compare the protocols.
+ if (!((u1.getProtocol() == u2.getProtocol()) ||
+ (u1.getProtocol() != null &&
+ u1.getProtocol().equalsIgnoreCase(u2.getProtocol()))))
+ return false;
+
+ // Compare the files.
+ if (!(u1.getFile() == u2.getFile() ||
+ (u1.getFile() != null && u1.getFile().equals(u2.getFile()))))
+ return false;
+
+ // Compare the ports.
+ int port1, port2;
+ port1 = (u1.getPort() != -1) ? u1.getPort() : u1.handler.getDefaultPort();
+ port2 = (u2.getPort() != -1) ? u2.getPort() : u2.handler.getDefaultPort();
+ if (port1 != port2)
+ return false;
+
+ // Compare the hosts.
+ if (!hostsEqual(u1, u2))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Get the IP address of our host. An empty host field or a DNS failure
+ * will result in a null return.
+ *
+ * @param u a URL object
+ * @return an {@code InetAddress} representing the host
+ * IP address.
+ * @since 1.3
+ */
+ protected synchronized InetAddress getHostAddress(URL u) {
+ if (u.hostAddress != null)
+ return u.hostAddress;
+
+ String host = u.getHost();
+ if (host == null || host.equals("")) {
+ return null;
+ } else {
+ try {
+ u.hostAddress = InetAddress.getByName(host);
+ } catch (UnknownHostException ex) {
+ return null;
+ } catch (SecurityException se) {
+ return null;
+ }
+ }
+ return u.hostAddress;
+ }
+
+ /**
+ * Compares the host components of two URLs.
+ * @param u1 the URL of the first host to compare
+ * @param u2 the URL of the second host to compare
+ * @return {@code true} if and only if they
+ * are equal, {@code false} otherwise.
+ * @since 1.3
+ */
+ protected boolean hostsEqual(URL u1, URL u2) {
+ // Android-changed: Don't compare the InetAddresses of the hosts.
+ if (u1.getHost() != null && u2.getHost() != null)
+ return u1.getHost().equalsIgnoreCase(u2.getHost());
+ else
+ return u1.getHost() == null && u2.getHost() == null;
+ }
+
+ /**
+ * Converts a {@code URL} of a specific protocol to a
+ * {@code String}.
+ *
+ * @param u the URL.
+ * @return a string representation of the {@code URL} argument.
+ */
+ protected String toExternalForm(URL u) {
+
+ // pre-compute length of StringBuffer
+ int len = u.getProtocol().length() + 1;
+ if (u.getAuthority() != null && u.getAuthority().length() > 0)
+ len += 2 + u.getAuthority().length();
+ if (u.getPath() != null) {
+ len += u.getPath().length();
+ }
+ if (u.getQuery() != null) {
+ len += 1 + u.getQuery().length();
+ }
+ if (u.getRef() != null)
+ len += 1 + u.getRef().length();
+
+ // BEGIN Android-changed: Add a toExternalForm variant that optionally escapes illegal chars
+ // TODO: The variant has been removed. We can potentially revert the change
+ StringBuilder result = new StringBuilder(len);
+ result.append(u.getProtocol());
+ result.append(":");
+ if (u.getAuthority() != null) {// ANDROID: && u.getAuthority().length() > 0) {
+ result.append("//");
+ result.append(u.getAuthority());
+ }
+ String fileAndQuery = u.getFile();
+ if (fileAndQuery != null) {
+ result.append(fileAndQuery);
+ }
+ // END Android-changed: Add a toExternalForm variant that optionally escapes illegal chars
+ if (u.getRef() != null) {
+ result.append("#");
+ result.append(u.getRef());
+ }
+ return result.toString();
+ }
+
+ // Android-changed: Removed @see tag (target is package-private):
+ // @see java.net.URL#set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String)
+ /**
+ * Sets the fields of the {@code URL} argument to the indicated values.
+ * Only classes derived from URLStreamHandler are able
+ * to use this method to set the values of the URL fields.
+ *
+ * @param u the URL to modify.
+ * @param protocol the protocol name.
+ * @param host the remote host value for the URL.
+ * @param port the port on the remote machine.
+ * @param authority the authority part for the URL.
+ * @param userInfo the userInfo part of the URL.
+ * @param path the path component of the URL.
+ * @param query the query part for the URL.
+ * @param ref the reference.
+ * @exception SecurityException if the protocol handler of the URL is
+ * different from this one
+ * @since 1.3
+ */
+ protected void setURL(URL u, String protocol, String host, int port,
+ String authority, String userInfo, String path,
+ String query, String ref) {
+ if (this != u.handler) {
+ throw new SecurityException("handler for url different from " +
+ "this handler");
+ }
+ // ensure that no one can reset the protocol on a given URL.
+ u.set(u.getProtocol(), host, port, authority, userInfo, path, query, ref);
+ }
+
+ /**
+ * Sets the fields of the {@code URL} argument to the indicated values.
+ * Only classes derived from URLStreamHandler are able
+ * to use this method to set the values of the URL fields.
+ *
+ * @param u the URL to modify.
+ * @param protocol the protocol name. This value is ignored since 1.2.
+ * @param host the remote host value for the URL.
+ * @param port the port on the remote machine.
+ * @param file the file.
+ * @param ref the reference.
+ * @exception SecurityException if the protocol handler of the URL is
+ * different from this one
+ * @deprecated Use setURL(URL, String, String, int, String, String, String,
+ * String);
+ */
+ @Deprecated
+ protected void setURL(URL u, String protocol, String host, int port,
+ String file, String ref) {
+ /*
+ * Only old URL handlers call this, so assume that the host
+ * field might contain "user:passwd@host". Fix as necessary.
+ */
+ String authority = null;
+ String userInfo = null;
+ if (host != null && host.length() != 0) {
+ authority = (port == -1) ? host : host + ":" + port;
+ int at = host.lastIndexOf('@');
+ if (at != -1) {
+ userInfo = host.substring(0, at);
+ host = host.substring(at+1);
+ }
+ }
+
+ /*
+ * Assume file might contain query part. Fix as necessary.
+ */
+ String path = null;
+ String query = null;
+ if (file != null) {
+ int q = file.lastIndexOf('?');
+ if (q != -1) {
+ query = file.substring(q+1);
+ path = file.substring(0, q);
+ } else
+ path = file;
+ }
+ setURL(u, protocol, host, port, authority, userInfo, path, query, ref);
+ }
+}
diff --git a/java/net/URLStreamHandlerFactory.java b/java/net/URLStreamHandlerFactory.java
new file mode 100644
index 0000000..e46e028
--- /dev/null
+++ b/java/net/URLStreamHandlerFactory.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+/**
+ * This interface defines a factory for {@code URL} stream
+ * protocol handlers.
+ * <p>
+ * It is used by the {@code URL} class to create a
+ * {@code URLStreamHandler} for a specific protocol.
+ *
+ * @author Arthur van Hoff
+ * @see java.net.URL
+ * @see java.net.URLStreamHandler
+ * @since JDK1.0
+ */
+public interface URLStreamHandlerFactory {
+ /**
+ * Creates a new {@code URLStreamHandler} instance with the specified
+ * protocol.
+ *
+ * @param protocol the protocol ("{@code ftp}",
+ * "{@code http}", "{@code nntp}", etc.).
+ * @return a {@code URLStreamHandler} for the specific protocol.
+ * @see java.net.URLStreamHandler
+ */
+ URLStreamHandler createURLStreamHandler(String protocol);
+}
diff --git a/java/net/UnknownHostException.java b/java/net/UnknownHostException.java
new file mode 100644
index 0000000..21a9d14
--- /dev/null
+++ b/java/net/UnknownHostException.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+
+/**
+ * Thrown to indicate that the IP address of a host could not be determined.
+ *
+ * @author Jonathan Payne
+ * @since JDK1.0
+ */
+public
+class UnknownHostException extends IOException {
+ private static final long serialVersionUID = -4639126076052875403L;
+
+ /**
+ * Constructs a new {@code UnknownHostException} with the
+ * specified detail message.
+ *
+ * @param host the detail message.
+ */
+ public UnknownHostException(String host) {
+ super(host);
+ }
+
+ /**
+ * Constructs a new {@code UnknownHostException} with no detail
+ * message.
+ */
+ public UnknownHostException() {
+ }
+}
diff --git a/java/net/UnknownServiceException.java b/java/net/UnknownServiceException.java
new file mode 100644
index 0000000..4eea4a7
--- /dev/null
+++ b/java/net/UnknownServiceException.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net;
+
+import java.io.IOException;
+
+/**
+ * Thrown to indicate that an unknown service exception has
+ * occurred. Either the MIME type returned by a URL connection does
+ * not make sense, or the application is attempting to write to a
+ * read-only URL connection.
+ *
+ * @author unascribed
+ * @since JDK1.0
+ */
+public class UnknownServiceException extends IOException {
+ private static final long serialVersionUID = -4169033248853639508L;
+
+ /**
+ * Constructs a new {@code UnknownServiceException} with no
+ * detail message.
+ */
+ public UnknownServiceException() {
+ }
+
+ /**
+ * Constructs a new {@code UnknownServiceException} with the
+ * specified detail message.
+ *
+ * @param msg the detail message.
+ */
+ public UnknownServiceException(String msg) {
+ super(msg);
+ }
+}
diff --git a/java/net/package-info.java b/java/net/package-info.java
new file mode 100644
index 0000000..fda2e4f
--- /dev/null
+++ b/java/net/package-info.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Provides the classes for implementing networking applications.
+ *
+ * <p> The java.net package can be roughly divided in two sections:</p>
+ * <ul>
+ * <li><p><i>A Low Level API</i>, which deals with the
+ * following abstractions:</p>
+ * <ul>
+ * <li><p><i>Addresses</i>, which are networking identifiers,
+ * like IP addresses.</p></li>
+ * <li><p><i>Sockets</i>, which are basic bidirectional data communication
+ * mechanisms.</p></li>
+ * <li><p><i>Interfaces</i>, which describe network interfaces. </p></li>
+ * </ul></li>
+ * <li> <p><i>A High Level API</i>, which deals with the following
+ * abstractions:</p>
+ * <ul>
+ * <li><p><i>URIs</i>, which represent
+ * Universal Resource Identifiers.</p></li>
+ * <li><p><i>URLs</i>, which represent
+ * Universal Resource Locators.</p></li>
+ * <li><p><i>Connections</i>, which represents connections to the resource
+ * pointed to by <i>URLs</i>.</p></li>
+ * </ul></li>
+ * </ul>
+ * <h2>Addresses</h2>
+ * <p>Addresses are used throughout the java.net APIs as either host
+ * identifiers, or socket endpoint identifiers.</p>
+ * <p>The {@link java.net.InetAddress} class is the abstraction representing an
+ * IP (Internet Protocol) address. It has two subclasses:
+ * <ul>
+ * <li>{@link java.net.Inet4Address} for IPv4 addresses.</li>
+ * <li>{@link java.net.Inet6Address} for IPv6 addresses.</li>
+ * </ul>
+ * <p>But, in most cases, there is no need to deal directly with the subclasses,
+ * as the InetAddress abstraction should cover most of the needed
+ * functionality.</p>
+ * <h3><b>About IPv6</b></h3>
+ * <p>Not all systems have support for the IPv6 protocol, and while the Java
+ * networking stack will attempt to detect it and use it transparently when
+ * available, it is also possible to disable its use with a system property.
+ * In the case where IPv6 is not available, or explicitly disabled,
+ * Inet6Address are not valid arguments for most networking operations any
+ * more. While methods like {@link java.net.InetAddress#getByName} are
+ * guaranteed not to return an Inet6Address when looking up host names, it
+ * is possible, by passing literals, to create such an object. In which
+ * case, most methods, when called with an Inet6Address will throw an
+ * Exception.</p>
+ * <h2>Sockets</h2>
+ * <p>Sockets are means to establish a communication link between machines over
+ * the network. The java.net package provides 4 kinds of Sockets:</p>
+ * <ul>
+ * <li>{@link java.net.Socket} is a TCP client API, and will typically
+ * be used to {@linkplain java.net.Socket#connect(SocketAddress)
+ * connect} to a remote host.</li>
+ * <li>{@link java.net.ServerSocket} is a TCP server API, and will
+ * typically {@linkplain java.net.ServerSocket#accept accept}
+ * connections from client sockets.</li>
+ * <li>{@link java.net.DatagramSocket} is a UDP endpoint API and is used
+ * to {@linkplain java.net.DatagramSocket#send send} and
+ * {@linkplain java.net.DatagramSocket#receive receive}
+ * {@linkplain java.net.DatagramPacket datagram packets}.</li>
+ * <li>{@link java.net.MulticastSocket} is a subclass of
+ * {@code DatagramSocket} used when dealing with multicast
+ * groups.</li>
+ * </ul>
+ * <p>Sending and receiving with TCP sockets is done through InputStreams and
+ * OutputStreams which can be obtained via the
+ * {@link java.net.Socket#getInputStream} and
+ * {@link java.net.Socket#getOutputStream} methods.</p>
+ * <h2>Interfaces</h2>
+ * <p>The {@link java.net.NetworkInterface} class provides APIs to browse and
+ * query all the networking interfaces (e.g. ethernet connection or PPP
+ * endpoint) of the local machine. It is through that class that you can
+ * check if any of the local interfaces is configured to support IPv6.</p>
+ * <p>Note, all conforming implementations must support at least one
+ * {@code NetworkInterface} object, which must either be connected to a
+ * network, or be a "loopback" interface that can only communicate with
+ * entities on the same machine.</p>
+ *
+ * <h2>High level API</h2>
+ * <p>A number of classes in the java.net package do provide for a much higher
+ * level of abstraction and allow for easy access to resources on the
+ * network. The classes are:
+ * <ul>
+ * <li>{@link java.net.URI} is the class representing a
+ * Universal Resource Identifier, as specified in RFC 2396.
+ * As the name indicates, this is just an Identifier and doesn't
+ * provide directly the means to access the resource.</li>
+ * <li>{@link java.net.URL} is the class representing a
+ * Universal Resource Locator, which is both an older concept for
+ * URIs and a means to access the resources.</li>
+ * <li>{@link java.net.URLConnection} is created from a URL and is the
+ * communication link used to access the resource pointed by the
+ * URL. This abstract class will delegate most of the work to the
+ * underlying protocol handlers like http or https.</li>
+ * <li>{@link java.net.HttpURLConnection} is a subclass of URLConnection
+ * and provides some additional functionalities specific to the
+ * HTTP protocol.</li>
+ * </ul>
+ * <p>The recommended usage is to use {@link java.net.URI} to identify
+ * resources, then convert it into a {@link java.net.URL} when it is time to
+ * access the resource. From that URL, you can either get the
+ * {@link java.net.URLConnection} for fine control, or get directly the
+ * InputStream.
+ * <p>Here is an example:</p>
+ * <pre>
+ * URI uri = new URI("http://java.sun.com/");
+ * URL url = uri.toURL();
+ * InputStream in = url.openStream();
+ * </pre>
+ * <h2>Protocol Handlers</h2>
+ * As mentioned, URL and URLConnection rely on protocol handlers which must be
+ * present, otherwise an Exception is thrown. This is the major difference with
+ * URIs which only identify resources, and therefore don't need to have access
+ * to the protocol handler. So, while it is possible to create an URI with any
+ * kind of protocol scheme (e.g. {@code myproto://myhost.mydomain/resource/}),
+ * a similar URL will try to instantiate the handler for the specified protocol;
+ * if it doesn't exist an exception will be thrown.
+ * <p>By default the protocol handlers are loaded dynamically from the default
+ * location. It is, however, possible to add to the search path by setting
+ * the {@code java.protocol.handler.pkgs} system property. For instance if
+ * it is set to {@code myapp.protocols}, then the URL code will try, in the
+ * case of http, first to load {@code myapp.protocols.http.Handler}, then,
+ * if this fails, {@code http.Handler} from the default location.
+ * <p>Note that the Handler class <b>has to</b> be a subclass of the abstract
+ * class {@link java.net.URLStreamHandler}.</p>
+ * <h2>Additional Specification</h2>
+ * <ul>
+ * <li><a href="doc-files/net-properties.html">
+ * Networking System Properties</a></li>
+ * </ul>
+ *
+ * @since JDK1.0
+ */
+package java.net;