Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9f1274b7 authored by Lorenzo Colitti's avatar Lorenzo Colitti
Browse files

Rework the per-network URL API.

This addresses API council comments.

Bug: 17112978
Change-Id: I698b243b2b685d1f25414cee72450be3ae0c2bf0
parent 223ddaa7
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -17212,12 +17212,12 @@ package android.net {
  }
  public class Network implements android.os.Parcelable {
    method public void bindSocket(java.net.Socket) throws java.io.IOException;
    method public int describeContents();
    method public java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
    method public java.net.URL getBoundURL(java.net.URL) throws java.net.MalformedURLException;
    method public java.net.InetAddress getByName(java.lang.String) throws java.net.UnknownHostException;
    method public javax.net.SocketFactory getSocketFactory();
    method public static void setNetworkBoundURLFactory(android.net.NetworkBoundURLFactory);
    method public java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException;
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator CREATOR;
  }
+49 −63
Original line number Diff line number Diff line
@@ -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) {
    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.getAllByName(host);
                        public InetAddress[] getAllByName(String host) throws UnknownHostException {
                            return Network.this.getAllByName(host);
                        }
                    };
                        network.mOkHttpClient = new OkHttpClient()
                                .setSocketFactory(network.getSocketFactory())
                    mOkHttpClient = new OkHttpClient()
                            .setSocketFactory(getSocketFactory())
                            .setHostResolver(hostResolver);
                }
            }
        }
    }

    /**
     * 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 URLConnection openConnection(URL url) throws IOException {
        maybeInitHttpClient();
        String protocol = url.getProtocol();
            URLStreamHandler handler = network.mOkHttpClient.createURLStreamHandler(protocol);
        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);
        return new URL(url, "", handler).openConnection();
    }
    };

    private static AtomicReference<NetworkBoundURLFactory> sNetworkBoundURLFactory =
            new AtomicReference <NetworkBoundURLFactory>(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}.
     * 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 URL getBoundURL(URL url) throws MalformedURLException {
        return sNetworkBoundURLFactory.get().getBoundURL(this, url);
    public void bindSocket(Socket socket) throws IOException {
        if (socket.isConnected()) {
            throw new SocketException("Socket is connected");
        }

    /**
     * Sets the {@link NetworkBoundURLFactory} to be used by future {@link #getBoundURL} calls.
     * If {@code null}, clears any factory that was previously specified.
     */
    public static void setNetworkBoundURLFactory(NetworkBoundURLFactory factory) {
        if (factory == null) {
            factory = DEFAULT_URL_FACTORY;
        // 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
+2 −1
Original line number Diff line number Diff line
@@ -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
+3 −3
Original line number Diff line number Diff line
@@ -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 },
};

+1 −2
Original line number Diff line number Diff line
@@ -639,8 +639,7 @@ public class NetworkMonitor extends StateMachine {
                log("Checking " + url.toString() + " on " +
                        mNetworkAgentInfo.networkInfo.getExtraInfo());
            }
            url = mNetworkAgentInfo.network.getBoundURL(url);
            urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url);
            urlConnection.setInstanceFollowRedirects(false);
            urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
            urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);