diff options
| author | Paul Jensen <pauljensen@google.com> | 2014-12-11 10:25:47 +0000 |
|---|---|---|
| committer | android-build-merger <android-build-merger@google.com> | 2014-12-11 10:25:47 +0000 |
| commit | 87dfb9ec25c79ffe34e98ce03ae2eb183b774615 (patch) | |
| tree | f3ad3610fa8a85f3b364af3ef60f534a2f501f20 | |
| parent | 072c7638afe9de3fe073cd0e824d41405e6401ff (diff) | |
| parent | 8f58207319fc2b73f83874892a544f8cf59cf7e9 (diff) | |
| download | frameworks_base-87dfb9ec25c79ffe34e98ce03ae2eb183b774615.zip frameworks_base-87dfb9ec25c79ffe34e98ce03ae2eb183b774615.tar.gz frameworks_base-87dfb9ec25c79ffe34e98ce03ae2eb183b774615.tar.bz2 | |
am 2122e14c: Merge "Fix several HTTP proxy issues with multinetworking." into lmp-mr1-dev automerge: d9d48cc
automerge: 8f58207
* commit '8f58207319fc2b73f83874892a544f8cf59cf7e9':
Fix several HTTP proxy issues with multinetworking.
7 files changed, 139 insertions, 42 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 6b4db10..bcd8fb4 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -44,7 +44,10 @@ import android.database.sqlite.SQLiteDebug.DbStats; import android.graphics.Bitmap; import android.graphics.Canvas; import android.hardware.display.DisplayManagerGlobal; +import android.net.ConnectivityManager; import android.net.IConnectivityManager; +import android.net.LinkProperties; +import android.net.Network; import android.net.Proxy; import android.net.ProxyInfo; import android.net.Uri; @@ -839,7 +842,13 @@ public final class ActivityThread { } public void setHttpProxy(String host, String port, String exclList, Uri pacFileUrl) { - Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl); + final Network network = ConnectivityManager.getProcessDefaultNetwork(); + if (network != null) { + Proxy.setHttpProxySystemProperty( + ConnectivityManager.from(getSystemContext()).getDefaultProxy()); + } else { + Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl); + } } public void processInBackground() { @@ -4430,7 +4439,7 @@ public final class ActivityThread { // crash if we can't get it. IConnectivityManager service = IConnectivityManager.Stub.asInterface(b); try { - ProxyInfo proxyInfo = service.getProxy(); + final ProxyInfo proxyInfo = service.getDefaultProxy(); Proxy.setHttpProxySystemProperty(proxyInfo); } catch (RemoteException e) {} } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 9dfb752..865e4a5 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -441,6 +441,13 @@ public class ConnectivityManager { public static final int NETID_UNSET = 0; private final IConnectivityManager mService; + /** + * A kludge to facilitate static access where a Context pointer isn't available, like in the + * case of the static set/getProcessDefaultNetwork methods and from the Network class. + * TODO: Remove this after deprecating the static methods in favor of non-static methods or + * methods that take a Context argument. + */ + private static ConnectivityManager sInstance; private INetworkManagementService mNMService; @@ -1394,6 +1401,7 @@ public class ConnectivityManager { */ public ConnectivityManager(IConnectivityManager service) { mService = checkNotNull(service, "missing IConnectivityManager"); + sInstance = this; } /** {@hide} */ @@ -1416,6 +1424,18 @@ public class ConnectivityManager { } /** + * @deprecated - use getSystemService. This is a kludge to support static access in certain + * situations where a Context pointer is unavailable. + * @hide + */ + public static ConnectivityManager getInstance() { + if (sInstance == null) { + throw new IllegalStateException("No ConnectivityManager yet constructed"); + } + return sInstance; + } + + /** * Get the set of tetherable, available interfaces. This list is limited by * device configuration and current interface existence. * @@ -1746,20 +1766,26 @@ public class ConnectivityManager { } /** - * Get the HTTP proxy settings for the current default network. Note that - * if a global proxy is set, it will override any per-network setting. + * Get the current default HTTP proxy settings. If a global proxy is set it will be returned, + * otherwise if this process is bound to a {@link Network} using + * {@link #setProcessDefaultNetwork} then that {@code Network}'s proxy is returned, otherwise + * the default network's proxy is returned. * * @return the {@link ProxyInfo} for the current HTTP proxy, or {@code null} if no * HTTP proxy is active. - * - * <p>This method requires the call to hold the permission - * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}. - * {@hide} - * @deprecated Deprecated in favor of {@link #getLinkProperties} + * @hide */ - public ProxyInfo getProxy() { + public ProxyInfo getDefaultProxy() { + final Network network = getProcessDefaultNetwork(); + if (network != null) { + final ProxyInfo globalProxy = getGlobalProxy(); + if (globalProxy != null) return globalProxy; + final LinkProperties lp = getLinkProperties(network); + if (lp != null) return lp.getHttpProxy(); + return null; + } try { - return mService.getProxy(); + return mService.getDefaultProxy(); } catch (RemoteException e) { return null; } @@ -2472,6 +2498,9 @@ public class ConnectivityManager { return true; } if (NetworkUtils.bindProcessToNetwork(netId)) { + // Set HTTP proxy system properties to match network. + // TODO: Deprecate this static method and replace it with a non-static version. + Proxy.setHttpProxySystemProperty(getInstance().getDefaultProxy()); // Must flush DNS cache as new network may have different DNS resolutions. InetAddress.clearDnsCache(); // Must flush socket pool as idle sockets will be bound to previous network and may diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index d9921a6..46af112 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -100,7 +100,7 @@ interface IConnectivityManager void setGlobalProxy(in ProxyInfo p); - ProxyInfo getProxy(); + ProxyInfo getDefaultProxy(); void setDataDependency(int networkType, boolean met); diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index 4fa0593..5c12696 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -27,6 +27,7 @@ import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.MalformedURLException; +import java.net.ProxySelector; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; @@ -244,16 +245,46 @@ public class Network implements Parcelable { * @see java.net.URL#openConnection() */ public URLConnection openConnection(URL url) throws IOException { + final ConnectivityManager cm = ConnectivityManager.getInstance(); + // TODO: Should this be optimized to avoid fetching the global proxy for every request? + ProxyInfo proxyInfo = cm.getGlobalProxy(); + if (proxyInfo == null) { + // TODO: Should this be optimized to avoid fetching LinkProperties for every request? + final LinkProperties lp = cm.getLinkProperties(this); + if (lp != null) proxyInfo = lp.getHttpProxy(); + } + java.net.Proxy proxy = null; + if (proxyInfo != null) { + proxy = proxyInfo.makeProxy(); + } else { + proxy = java.net.Proxy.NO_PROXY; + } + return openConnection(url, proxy); + } + + /** + * 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}. + * + * @param proxy the proxy through which the connection will be established. + * @return a {@code URLConnection} to the resource referred to by this URL. + * @throws MalformedURLException if the URL protocol is not HTTP or HTTPS. + * @throws IllegalArgumentException if the argument proxy is null. + * @throws IOException if an error occurs while opening the connection. + * @see java.net.URL#openConnection() + * @hide + */ + public URLConnection openConnection(URL url, java.net.Proxy proxy) throws IOException { + if (proxy == null) throw new IllegalArgumentException("proxy is null"); maybeInitHttpClient(); String protocol = url.getProtocol(); OkHttpClient client; // TODO: HttpHandler creates OkHttpClients that share the default ResponseCache. // Could this cause unexpected behavior? - // TODO: Should the network's proxy be specified? if (protocol.equals("http")) { - client = HttpHandler.createHttpOkHttpClient(null /* proxy */); + client = HttpHandler.createHttpOkHttpClient(proxy); } else if (protocol.equals("https")) { - client = HttpsHandler.createHttpsOkHttpClient(null /* proxy */); + client = HttpsHandler.createHttpsOkHttpClient(proxy); } else { // OkHttpClient only supports HTTP and HTTPS and returns a null URLStreamHandler if // passed another protocol. diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java index 7694420..a3cad77 100644 --- a/core/java/android/net/ProxyInfo.java +++ b/core/java/android/net/ProxyInfo.java @@ -260,7 +260,8 @@ public class ProxyInfo implements Parcelable { if (!Uri.EMPTY.equals(mPacFileUrl)) { sb.append("PAC Script: "); sb.append(mPacFileUrl); - } else if (mHost != null) { + } + if (mHost != null) { sb.append("["); sb.append(mHost); sb.append("] "); diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java index 1c7b033..7253579 100644 --- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java +++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java @@ -23,12 +23,10 @@ import android.content.Intent; import android.graphics.Bitmap; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; -import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.Proxy; -import android.net.ProxyInfo; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; @@ -96,26 +94,10 @@ public class CaptivePortalLoginActivity extends Activity { done(CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS); } + final ConnectivityManager cm = ConnectivityManager.from(this); final Network network = new Network(mNetId); - ConnectivityManager.setProcessDefaultNetwork(network); - - // Set HTTP proxy system properties to those of the selected Network. - final LinkProperties lp = ConnectivityManager.from(this).getLinkProperties(network); - if (lp != null) { - final ProxyInfo proxyInfo = lp.getHttpProxy(); - String host = ""; - String port = ""; - String exclList = ""; - Uri pacFileUrl = Uri.EMPTY; - if (proxyInfo != null) { - host = proxyInfo.getHost(); - port = Integer.toString(proxyInfo.getPort()); - exclList = proxyInfo.getExclusionListAsString(); - pacFileUrl = proxyInfo.getPacFileUrl(); - } - Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl); - Log.v(TAG, "Set proxy system properties to " + proxyInfo); - } + // Also initializes proxy system properties. + cm.setProcessDefaultNetwork(network); // Proxy system properties must be initialized before setContentView is called because // setContentView initializes the WebView logic which in turn reads the system properties. @@ -124,8 +106,7 @@ public class CaptivePortalLoginActivity extends Activity { getActionBar().setDisplayShowHomeEnabled(false); // Exit app if Network disappears. - final NetworkCapabilities networkCapabilities = - ConnectivityManager.from(this).getNetworkCapabilities(network); + final NetworkCapabilities networkCapabilities = cm.getNetworkCapabilities(network); if (networkCapabilities == null) { finish(); return; @@ -140,7 +121,7 @@ public class CaptivePortalLoginActivity extends Activity { for (int transportType : networkCapabilities.getTransportTypes()) { builder.addTransportType(transportType); } - ConnectivityManager.from(this).registerNetworkCallback(builder.build(), mNetworkCallback); + cm.registerNetworkCallback(builder.build(), mNetworkCallback); final WebView myWebView = (WebView) findViewById(R.id.webview); myWebView.clearCache(true); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index e11fa93..499cff3 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2585,7 +2585,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - public ProxyInfo getProxy() { + public ProxyInfo getDefaultProxy() { // this information is already available as a world read/writable jvm property // so this API change wouldn't have a benifit. It also breaks the passing // of proxy info to all the JVMs. @@ -2597,6 +2597,34 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + // Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present + // (e.g. if mGlobalProxy==null fall back to network-specific proxy, if network-specific + // proxy is null then there is no proxy in place). + private ProxyInfo canonicalizeProxyInfo(ProxyInfo proxy) { + if (proxy != null && TextUtils.isEmpty(proxy.getHost()) + && (proxy.getPacFileUrl() == null || Uri.EMPTY.equals(proxy.getPacFileUrl()))) { + proxy = null; + } + return proxy; + } + + // ProxyInfo equality function with a couple modifications over ProxyInfo.equals() to make it + // better for determining if a new proxy broadcast is necessary: + // 1. Canonicalize empty ProxyInfos to null so an empty proxy compares equal to null so as to + // avoid unnecessary broadcasts. + // 2. Make sure all parts of the ProxyInfo's compare true, including the host when a PAC URL + // is in place. This is important so legacy PAC resolver (see com.android.proxyhandler) + // changes aren't missed. The legacy PAC resolver pretends to be a simple HTTP proxy but + // actually uses the PAC to resolve; this results in ProxyInfo's with PAC URL, host and port + // all set. + private boolean proxyInfoEqual(ProxyInfo a, ProxyInfo b) { + a = canonicalizeProxyInfo(a); + b = canonicalizeProxyInfo(b); + // ProxyInfo.equals() doesn't check hosts when PAC URLs are present, but we need to check + // hosts even when PAC URLs are present to account for the legacy PAC resolver. + return Objects.equals(a, b) && (a == null || Objects.equals(a.getHost(), b.getHost())); + } + public void setGlobalProxy(ProxyInfo proxyProperties) { enforceConnectivityInternalPermission(); @@ -2714,6 +2742,20 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + // If the proxy has changed from oldLp to newLp, resend proxy broadcast with default proxy. + // This method gets called when any network changes proxy, but the broadcast only ever contains + // the default proxy (even if it hasn't changed). + // TODO: Deprecate the broadcast extras as they aren't necessarily applicable in a multi-network + // world where an app might be bound to a non-default network. + private void updateProxy(LinkProperties newLp, LinkProperties oldLp, NetworkAgentInfo nai) { + ProxyInfo newProxyInfo = newLp == null ? null : newLp.getHttpProxy(); + ProxyInfo oldProxyInfo = oldLp == null ? null : oldLp.getHttpProxy(); + + if (!proxyInfoEqual(newProxyInfo, oldProxyInfo)) { + sendProxyBroadcast(getDefaultProxy()); + } + } + private void handleDeprecatedGlobalHttpProxy() { String proxy = Settings.Global.getString(mContext.getContentResolver(), Settings.Global.HTTP_PROXY); @@ -3627,7 +3669,11 @@ public class ConnectivityService extends IConnectivityManager.Stub updateDnses(newLp, oldLp, netId, flushDns, useDefaultDns); updateClat(newLp, oldLp, networkAgent); - if (isDefaultNetwork(networkAgent)) handleApplyDefaultProxy(newLp.getHttpProxy()); + if (isDefaultNetwork(networkAgent)) { + handleApplyDefaultProxy(newLp.getHttpProxy()); + } else { + updateProxy(newLp, oldLp, networkAgent); + } // TODO - move this check to cover the whole function if (!Objects.equals(newLp, oldLp)) { notifyIfacesChanged(); |
