diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index 0de3f2606b..d2a4728fb0 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -16,10 +16,10 @@ package android.net; -import android.net.NetworkBoundURLFactory; import android.net.NetworkUtils; import android.os.Parcelable; import android.os.Parcel; +import android.system.ErrnoException; import java.io.IOException; import java.net.InetAddress; @@ -30,6 +30,7 @@ import java.net.SocketAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.net.URL; +import java.net.URLConnection; import java.net.URLStreamHandler; import java.util.concurrent.atomic.AtomicReference; import javax.net.SocketFactory; @@ -54,7 +55,7 @@ public class Network implements Parcelable { public final int netId; // Objects used to perform per-network operations such as getSocketFactory - // and getBoundURL, and a lock to protect access to them. + // and openConnection, and a lock to protect access to them. private volatile NetworkBoundSocketFactory mNetworkBoundSocketFactory = null; private volatile OkHttpClient mOkHttpClient = null; private Object mLock = new Object(); @@ -157,12 +158,7 @@ public class Network implements Parcelable { @Override public Socket createSocket() throws IOException { Socket socket = new Socket(); - // Query a property of the underlying socket to ensure the underlying - // socket exists so a file descriptor is available to bind to a network. - socket.getReuseAddress(); - if (!NetworkUtils.bindSocketToNetwork(socket.getFileDescriptor$().getInt$(), mNetId)) { - throw new SocketException("Failed to bind socket to network."); - } + bindSocket(socket); return socket; } } @@ -187,73 +183,63 @@ public class Network implements Parcelable { return mNetworkBoundSocketFactory; } - /** The default NetworkBoundURLFactory, used if setNetworkBoundURLFactory is never called. */ - private static final NetworkBoundURLFactory DEFAULT_URL_FACTORY = new NetworkBoundURLFactory() { - public URL getBoundURL(final Network network, URL url) throws MalformedURLException { - if (network.mOkHttpClient == null) { - synchronized (network.mLock) { - if (network.mOkHttpClient == null) { - HostResolver hostResolver = new HostResolver() { - @Override - public InetAddress[] getAllByName(String host) - throws UnknownHostException { - return network.getAllByName(host); - } - }; - network.mOkHttpClient = new OkHttpClient() - .setSocketFactory(network.getSocketFactory()) - .setHostResolver(hostResolver); - } + private void maybeInitHttpClient() { + if (mOkHttpClient == null) { + synchronized (mLock) { + if (mOkHttpClient == null) { + HostResolver hostResolver = new HostResolver() { + @Override + public InetAddress[] getAllByName(String host) throws UnknownHostException { + return Network.this.getAllByName(host); + } + }; + mOkHttpClient = new OkHttpClient() + .setSocketFactory(getSocketFactory()) + .setHostResolver(hostResolver); } } - - String protocol = url.getProtocol(); - URLStreamHandler handler = network.mOkHttpClient.createURLStreamHandler(protocol); - if (handler == null) { - // OkHttpClient only supports HTTP and HTTPS and returns a null URLStreamHandler if - // passed another protocol. - throw new MalformedURLException("Invalid URL or unrecognized protocol " + protocol); - } - return new URL(url, "", handler); } - }; - - private static AtomicReference sNetworkBoundURLFactory = - new AtomicReference (DEFAULT_URL_FACTORY); - - /** - * Returns a {@link URL} based on the given URL but bound to this {@code Network}, - * such that opening the URL will send all network traffic on this Network. - * - * Note that if this {@code Network} ever disconnects, any URL object generated by this method - * in the past or future will cease to work. - * - * The returned URL may have a {@link URLStreamHandler} explicitly set, which may not be the - * handler generated by the factory set with {@link java.net.URL#setURLStreamHandlerFactory}. To - * affect the {@code URLStreamHandler}s of URLs returned by this method, call - * {@link #setNetworkBoundURLFactory}. - * - * Because the returned URLs may have an explicit {@code URLStreamHandler} set, using them as a - * context when constructing other URLs and explicitly specifying a {@code URLStreamHandler} may - * result in URLs that are no longer bound to the same {@code Network}. - * - * The default implementation only supports {@code HTTP} and {@code HTTPS} URLs. - * - * @return a {@link URL} bound to this {@code Network}. - */ - public URL getBoundURL(URL url) throws MalformedURLException { - return sNetworkBoundURLFactory.get().getBoundURL(this, url); } /** - * Sets the {@link NetworkBoundURLFactory} to be used by future {@link #getBoundURL} calls. - * If {@code null}, clears any factory that was previously specified. + * Opens the specified {@link URL} on this {@code Network}, such that all traffic will be sent + * on this Network. The URL protocol must be {@code HTTP} or {@code HTTPS}. + * + * @return a {@code URLConnection} to the resource referred to by this URL. + * @throws MalformedURLException if the URL protocol is not HTTP or HTTPS. + * @throws IOException if an error occurs while opening the connection. + * @see java.net.URL#openConnection() */ - public static void setNetworkBoundURLFactory(NetworkBoundURLFactory factory) { - if (factory == null) { - factory = DEFAULT_URL_FACTORY; + public URLConnection openConnection(URL url) throws IOException { + maybeInitHttpClient(); + String protocol = url.getProtocol(); + URLStreamHandler handler = mOkHttpClient.createURLStreamHandler(protocol); + if (handler == null) { + // OkHttpClient only supports HTTP and HTTPS and returns a null URLStreamHandler if + // passed another protocol. + throw new MalformedURLException("Invalid URL or unrecognized protocol " + protocol); + } + return new URL(url, "", handler).openConnection(); + } + + /** + * Binds the specified {@link Socket} to this {@code Network}. All data traffic on the socket + * will be sent on this {@code Network}, irrespective of any process-wide network binding set by + * {@link ConnectivityManager#setProcessDefaultNetwork}. The socket must not be connected. + */ + public void bindSocket(Socket socket) throws IOException { + if (socket.isConnected()) { + throw new SocketException("Socket is connected"); + } + // Query a property of the underlying socket to ensure the underlying + // socket exists so a file descriptor is available to bind to a network. + socket.getReuseAddress(); + int err = NetworkUtils.bindSocketToNetwork(socket.getFileDescriptor$().getInt$(), netId); + if (err != 0) { + // bindSocketToNetwork returns negative errno. + throw new ErrnoException("Binding socket to network " + netId, -err) + .rethrowAsSocketException(); } - sNetworkBoundURLFactory.set(factory); } // implement the Parcelable interface diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index 54d8676be5..d2a29975c3 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -128,8 +128,9 @@ public class NetworkUtils { /** * Explicitly binds {@code socketfd} to the network designated by {@code netId}. This * overrides any binding via {@link #bindProcessToNetwork}. + * @return 0 on success or negative errno on failure. */ - public native static boolean bindSocketToNetwork(int socketfd, int netId); + public native static int bindSocketToNetwork(int socketfd, int netId); /** * Protect {@code socketfd} from VPN connections. After protecting, data sent through diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index 5bd38f381c..8b9f5744d2 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -236,10 +236,10 @@ static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv * return (jboolean) !setNetworkForResolv(netId); } -static jboolean android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket, +static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket, jint netId) { - return (jboolean) !setNetworkForSocket(netId, socket); + return setNetworkForSocket(netId, socket); } static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket) @@ -263,7 +263,7 @@ static JNINativeMethod gNetworkUtilMethods[] = { { "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork }, { "getNetworkBoundToProcess", "()I", (void*) android_net_utils_getNetworkBoundToProcess }, { "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution }, - { "bindSocketToNetwork", "(II)Z", (void*) android_net_utils_bindSocketToNetwork }, + { "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork }, { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn }, };