Rework the per-network URL API.
This addresses API council comments. Bug: 17112978 Change-Id: I698b243b2b685d1f25414cee72450be3ae0c2bf0
This commit is contained in:
@@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
package android.net;
|
package android.net;
|
||||||
|
|
||||||
import android.net.NetworkBoundURLFactory;
|
|
||||||
import android.net.NetworkUtils;
|
import android.net.NetworkUtils;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
|
import android.system.ErrnoException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
@@ -30,6 +30,7 @@ import java.net.SocketAddress;
|
|||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
import java.net.URLStreamHandler;
|
import java.net.URLStreamHandler;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
@@ -54,7 +55,7 @@ public class Network implements Parcelable {
|
|||||||
public final int netId;
|
public final int netId;
|
||||||
|
|
||||||
// Objects used to perform per-network operations such as getSocketFactory
|
// 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 NetworkBoundSocketFactory mNetworkBoundSocketFactory = null;
|
||||||
private volatile OkHttpClient mOkHttpClient = null;
|
private volatile OkHttpClient mOkHttpClient = null;
|
||||||
private Object mLock = new Object();
|
private Object mLock = new Object();
|
||||||
@@ -157,12 +158,7 @@ public class Network implements Parcelable {
|
|||||||
@Override
|
@Override
|
||||||
public Socket createSocket() throws IOException {
|
public Socket createSocket() throws IOException {
|
||||||
Socket socket = new Socket();
|
Socket socket = new Socket();
|
||||||
// Query a property of the underlying socket to ensure the underlying
|
bindSocket(socket);
|
||||||
// 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.");
|
|
||||||
}
|
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,73 +183,63 @@ public class Network implements Parcelable {
|
|||||||
return mNetworkBoundSocketFactory;
|
return mNetworkBoundSocketFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The default NetworkBoundURLFactory, used if setNetworkBoundURLFactory is never called. */
|
private void maybeInitHttpClient() {
|
||||||
private static final NetworkBoundURLFactory DEFAULT_URL_FACTORY = new NetworkBoundURLFactory() {
|
if (mOkHttpClient == null) {
|
||||||
public URL getBoundURL(final Network network, URL url) throws MalformedURLException {
|
synchronized (mLock) {
|
||||||
if (network.mOkHttpClient == null) {
|
if (mOkHttpClient == null) {
|
||||||
synchronized (network.mLock) {
|
|
||||||
if (network.mOkHttpClient == null) {
|
|
||||||
HostResolver hostResolver = new HostResolver() {
|
HostResolver hostResolver = new HostResolver() {
|
||||||
@Override
|
@Override
|
||||||
public InetAddress[] getAllByName(String host)
|
public InetAddress[] getAllByName(String host) throws UnknownHostException {
|
||||||
throws UnknownHostException {
|
return Network.this.getAllByName(host);
|
||||||
return network.getAllByName(host);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
network.mOkHttpClient = new OkHttpClient()
|
mOkHttpClient = new OkHttpClient()
|
||||||
.setSocketFactory(network.getSocketFactory())
|
.setSocketFactory(getSocketFactory())
|
||||||
.setHostResolver(hostResolver);
|
.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();
|
String protocol = url.getProtocol();
|
||||||
URLStreamHandler handler = network.mOkHttpClient.createURLStreamHandler(protocol);
|
URLStreamHandler handler = mOkHttpClient.createURLStreamHandler(protocol);
|
||||||
if (handler == null) {
|
if (handler == null) {
|
||||||
// OkHttpClient only supports HTTP and HTTPS and returns a null URLStreamHandler if
|
// OkHttpClient only supports HTTP and HTTPS and returns a null URLStreamHandler if
|
||||||
// passed another protocol.
|
// passed another protocol.
|
||||||
throw new MalformedURLException("Invalid URL or unrecognized protocol " + 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}.
|
|
||||||
*/
|
|
||||||
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.
|
* Binds the specified {@link Socket} to this {@code Network}. All data traffic on the socket
|
||||||
* If {@code null}, clears any factory that was previously specified.
|
* 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 static void setNetworkBoundURLFactory(NetworkBoundURLFactory factory) {
|
public void bindSocket(Socket socket) throws IOException {
|
||||||
if (factory == null) {
|
if (socket.isConnected()) {
|
||||||
factory = DEFAULT_URL_FACTORY;
|
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
|
// implement the Parcelable interface
|
||||||
|
|||||||
@@ -128,8 +128,9 @@ public class NetworkUtils {
|
|||||||
/**
|
/**
|
||||||
* Explicitly binds {@code socketfd} to the network designated by {@code netId}. This
|
* Explicitly binds {@code socketfd} to the network designated by {@code netId}. This
|
||||||
* overrides any binding via {@link #bindProcessToNetwork}.
|
* 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
|
* Protect {@code socketfd} from VPN connections. After protecting, data sent through
|
||||||
|
|||||||
@@ -236,10 +236,10 @@ static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *
|
|||||||
return (jboolean) !setNetworkForResolv(netId);
|
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)
|
jint netId)
|
||||||
{
|
{
|
||||||
return (jboolean) !setNetworkForSocket(netId, socket);
|
return setNetworkForSocket(netId, socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint 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 },
|
{ "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
|
||||||
{ "getNetworkBoundToProcess", "()I", (void*) android_net_utils_getNetworkBoundToProcess },
|
{ "getNetworkBoundToProcess", "()I", (void*) android_net_utils_getNetworkBoundToProcess },
|
||||||
{ "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
|
{ "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 },
|
{ "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user