Network: Use HttpURLConnectionFactory rather than OkHttp APIs

This is a refactoring with no behavior change.

The new class provides an abstraction layer to hide knowledge
about OkHttp-specific APIs. Logic from android.net.Network that
knew about OkHttp is moving into that abstraction layer.

This CL refactors android.net.Network to make use of this
abstraction layer instead of the tight coupling onto OkHttp
APIs. The class no longer imports any classes from okhttp
packages.

The values of mDns and mConnectionPool, which never change after
the initial call to maybeInitHttpClient(), are now set directly on
the AndroidHttpClient instance when it is constructed in that method.

Applications can overwrite getSocketFactory() and might depend on
that method being called (and the result used) every time a
connection is opened; therefore, for maximum app compatibility this
call was kept inside openConnection().

This CL is a prerequisite for introducing an additional frameworks
dependency on a richer API than HttpURLConnection.

Test: Build and install apk for FrameworksCoreTests, then run:
      adb shell am instrument -e class android.net.NetworkTest -w com.android.frameworks.coretests
Bug: 64021405

Change-Id: I2c73d260508ee20c6a40fd6e95e2d058d3ea2330
This commit is contained in:
Tobias Thierer
2017-04-11 22:16:53 +01:00
parent 39a0b6b58e
commit 5efcea8a39

View File

@@ -22,6 +22,9 @@ import android.system.ErrnoException;
import android.system.Os; import android.system.Os;
import android.system.OsConstants; import android.system.OsConstants;
import libcore.net.http.Dns;
import libcore.net.http.HttpURLConnectionFactory;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.IOException; import java.io.IOException;
import java.net.DatagramSocket; import java.net.DatagramSocket;
@@ -35,18 +38,9 @@ import java.net.UnknownHostException;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.net.SocketFactory; import javax.net.SocketFactory;
import com.android.okhttp.ConnectionPool;
import com.android.okhttp.Dns;
import com.android.okhttp.HttpHandler;
import com.android.okhttp.HttpsHandler;
import com.android.okhttp.OkHttpClient;
import com.android.okhttp.OkUrlFactory;
import com.android.okhttp.internal.Internal;
/** /**
* Identifies a {@code Network}. This is supplied to applications via * Identifies a {@code Network}. This is supplied to applications via
* {@link ConnectivityManager.NetworkCallback} in response to the active * {@link ConnectivityManager.NetworkCallback} in response to the active
@@ -66,10 +60,9 @@ public class Network implements Parcelable {
// Objects used to perform per-network operations such as getSocketFactory // Objects used to perform per-network operations such as getSocketFactory
// and openConnection, 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;
// mLock should be used to control write access to mConnectionPool and mDns. // mLock should be used to control write access to mUrlConnectionFactory.
// maybeInitHttpClient() must be called prior to reading either variable. // maybeInitUrlConnectionFactory() must be called prior to reading this field.
private volatile ConnectionPool mConnectionPool = null; private volatile HttpURLConnectionFactory mUrlConnectionFactory;
private volatile Dns mDns = null;
private final Object mLock = new Object(); private final Object mLock = new Object();
// Default connection pool values. These are evaluated at startup, just // Default connection pool values. These are evaluated at startup, just
@@ -221,19 +214,19 @@ public class Network implements Parcelable {
// will be instantiated in the near future with the same NetID. A good // will be instantiated in the near future with the same NetID. A good
// solution would involve purging empty (or when all connections are timed // solution would involve purging empty (or when all connections are timed
// out) ConnectionPools. // out) ConnectionPools.
private void maybeInitHttpClient() { private void maybeInitUrlConnectionFactory() {
synchronized (mLock) { synchronized (mLock) {
if (mDns == null) { if (mUrlConnectionFactory == null) {
mDns = new Dns() { // Set configuration on the HttpURLConnectionFactory that will be good for all
@Override // connections created by this Network. Configuration that might vary is left
public List<InetAddress> lookup(String hostname) throws UnknownHostException { // until openConnection() and passed as arguments.
return Arrays.asList(Network.this.getAllByName(hostname)); Dns dnsLookup = hostname -> Arrays.asList(Network.this.getAllByName(hostname));
} HttpURLConnectionFactory urlConnectionFactory = new HttpURLConnectionFactory();
}; urlConnectionFactory.setDns(dnsLookup); // Let traffic go via dnsLookup
} // A private connection pool just for this Network.
if (mConnectionPool == null) { urlConnectionFactory.setNewConnectionPool(httpMaxConnections,
mConnectionPool = new ConnectionPool(httpMaxConnections,
httpKeepAliveDurationMs, TimeUnit.MILLISECONDS); httpKeepAliveDurationMs, TimeUnit.MILLISECONDS);
mUrlConnectionFactory = urlConnectionFactory;
} }
} }
} }
@@ -254,7 +247,7 @@ public class Network implements Parcelable {
} }
// TODO: Should this be optimized to avoid fetching the global proxy for every request? // TODO: Should this be optimized to avoid fetching the global proxy for every request?
final ProxyInfo proxyInfo = cm.getProxyForNetwork(this); final ProxyInfo proxyInfo = cm.getProxyForNetwork(this);
java.net.Proxy proxy = null; final java.net.Proxy proxy;
if (proxyInfo != null) { if (proxyInfo != null) {
proxy = proxyInfo.makeProxy(); proxy = proxyInfo.makeProxy();
} else { } else {
@@ -276,26 +269,9 @@ public class Network implements Parcelable {
*/ */
public URLConnection openConnection(URL url, java.net.Proxy proxy) throws IOException { public URLConnection openConnection(URL url, java.net.Proxy proxy) throws IOException {
if (proxy == null) throw new IllegalArgumentException("proxy is null"); if (proxy == null) throw new IllegalArgumentException("proxy is null");
maybeInitHttpClient(); maybeInitUrlConnectionFactory();
String protocol = url.getProtocol(); SocketFactory socketFactory = getSocketFactory();
OkUrlFactory okUrlFactory; return mUrlConnectionFactory.openConnection(url, socketFactory, proxy);
// TODO: HttpHandler creates OkUrlFactory instances that share the default ResponseCache.
// Could this cause unexpected behavior?
if (protocol.equals("http")) {
okUrlFactory = HttpHandler.createHttpOkUrlFactory(proxy);
} else if (protocol.equals("https")) {
okUrlFactory = HttpsHandler.createHttpsOkUrlFactory(proxy);
} else {
// OkHttp only supports HTTP and HTTPS and returns a null URLStreamHandler if
// passed another protocol.
throw new MalformedURLException("Invalid URL or unrecognized protocol " + protocol);
}
OkHttpClient client = okUrlFactory.client();
client.setSocketFactory(getSocketFactory()).setConnectionPool(mConnectionPool);
// Let network traffic go via mDns
client.setDns(mDns);
return okUrlFactory.open(url);
} }
/** /**