Add PAC File support for proxy configuration

PAC (Proxy auto-config) files contain a single javascript function,
FindProxyForURL(url, host).  It gets called to determine what proxy should be
used for a specific request.

This adds PAC support to the system.  The ProxyProperties has been modified
to hold the PAC file when one is present.  The Proxy method
setHttpProxySystemProperty has been modified to insert a PacProxySelector
as the default ProxySelector when it is required.  This new ProxySelector
makes calls to the ConnectivityService to parse the PAC file.

The ConnectivityService and the WifiConfigStore have been modified to support
saving the extra PAC file data.

The ConnectivityService now has a class attached (PacProxyNative) that
interfaces to the native calls for PAC files.  The parsing of the PAC file
is handled by libpac (which is being added to external/) which utilizes
libv8 to parse the javascript.

As a fallback to applications that don't use the java ProxySelector,  the proxy
is setup to point to a local proxy server that will handle the pac parsing.

bug:10182711
Change-Id: I5eb8df893c632fd3e1b732385cb7720ad646f401
This commit is contained in:
Jason Monk
2013-07-03 17:04:33 -04:00
parent 4b61d6c0c4
commit a5bf2840cc
2 changed files with 71 additions and 14 deletions

View File

@@ -20,9 +20,7 @@ package android.net;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Locale;
@@ -38,17 +36,30 @@ public class ProxyProperties implements Parcelable {
private String mExclusionList;
private String[] mParsedExclusionList;
private String mPacFileUrl;
public static final String LOCAL_EXCL_LIST = "";
public static final int LOCAL_PORT = 8182;
public static final String LOCAL_HOST = "localhost";
public ProxyProperties(String host, int port, String exclList) {
mHost = host;
mPort = port;
setExclusionList(exclList);
}
public ProxyProperties(String pacFileUrl) {
mHost = LOCAL_HOST;
mPort = LOCAL_PORT;
setExclusionList(LOCAL_EXCL_LIST);
mPacFileUrl = pacFileUrl;
}
private ProxyProperties(String host, int port, String exclList, String[] parsedExclList) {
mHost = host;
mPort = port;
mExclusionList = exclList;
mParsedExclusionList = parsedExclList;
mPacFileUrl = null;
}
// copy constructor instead of clone
@@ -56,6 +67,7 @@ public class ProxyProperties implements Parcelable {
if (source != null) {
mHost = source.getHost();
mPort = source.getPort();
mPacFileUrl = source.getPacFileUrl();
mExclusionList = source.getExclusionList();
mParsedExclusionList = source.mParsedExclusionList;
}
@@ -69,6 +81,10 @@ public class ProxyProperties implements Parcelable {
return inetSocketAddress;
}
public String getPacFileUrl() {
return mPacFileUrl;
}
public String getHost() {
return mHost;
}
@@ -130,7 +146,10 @@ public class ProxyProperties implements Parcelable {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (mHost != null) {
if (mPacFileUrl != null) {
sb.append("PAC Script: ");
sb.append(mPacFileUrl);
} else if (mHost != null) {
sb.append("[");
sb.append(mHost);
sb.append("] ");
@@ -148,6 +167,14 @@ public class ProxyProperties implements Parcelable {
public boolean equals(Object o) {
if (!(o instanceof ProxyProperties)) return false;
ProxyProperties p = (ProxyProperties)o;
// If PAC URL is present in either then they must be equal.
// Other parameters will only be for fall back.
if (!TextUtils.isEmpty(mPacFileUrl)) {
return mPacFileUrl.equals(p.getPacFileUrl());
}
if (!TextUtils.isEmpty(p.getPacFileUrl())) {
return false;
}
if (mExclusionList != null && !mExclusionList.equals(p.getExclusionList())) return false;
if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) {
return false;
@@ -181,6 +208,13 @@ public class ProxyProperties implements Parcelable {
* @hide
*/
public void writeToParcel(Parcel dest, int flags) {
if (mPacFileUrl != null) {
dest.writeByte((byte)1);
dest.writeString(mPacFileUrl);
return;
} else {
dest.writeByte((byte)0);
}
if (mHost != null) {
dest.writeByte((byte)1);
dest.writeString(mHost);
@@ -201,7 +235,10 @@ public class ProxyProperties implements Parcelable {
public ProxyProperties createFromParcel(Parcel in) {
String host = null;
int port = 0;
if (in.readByte() == 1) {
if (in.readByte() != 0) {
return new ProxyProperties(in.readString());
}
if (in.readByte() != 0) {
host = in.readString();
port = in.readInt();
}

View File

@@ -56,13 +56,11 @@ import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Uri;
import android.net.LinkProperties.CompareResult;
import android.net.MobileDataStateTracker;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
import android.net.NetworkQuotaInfo;
import android.net.NetworkState;
import android.net.NetworkStateTracker;
@@ -70,6 +68,7 @@ import android.net.NetworkUtils;
import android.net.Proxy;
import android.net.ProxyProperties;
import android.net.RouteInfo;
import android.net.Uri;
import android.net.wifi.WifiStateTracker;
import android.net.wimax.WimaxManagerConstants;
import android.os.AsyncTask;
@@ -102,6 +101,7 @@ import android.util.SparseIntArray;
import android.util.Xml;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
@@ -110,9 +110,11 @@ import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import com.android.net.IProxyService;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.PacManager;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.BaseNetworkObserver;
@@ -120,8 +122,6 @@ import com.android.server.net.LockdownVpnTracker;
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
import com.android.internal.annotations.GuardedBy;
import dalvik.system.DexClassLoader;
import org.xmlpull.v1.XmlPullParser;
@@ -370,6 +370,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// track the global proxy.
private ProxyProperties mGlobalProxy = null;
private PacManager mPacManager = null;
private SettingsObserver mSettingsObserver;
NetworkConfig[] mNetConfigs;
@@ -631,6 +633,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mDataConnectionStats = new DataConnectionStats(mContext);
mDataConnectionStats.startMonitoring();
mPacManager = new PacManager(mContext);
}
/**
@@ -3168,13 +3172,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// of proxy info to all the JVMs.
// enforceAccessPermission();
synchronized (mProxyLock) {
if (mGlobalProxy != null) return mGlobalProxy;
return (mDefaultProxyDisabled ? null : mDefaultProxy);
ProxyProperties ret = mGlobalProxy;
if ((ret == null) && !mDefaultProxyDisabled) ret = mDefaultProxy;
return ret;
}
}
public void setGlobalProxy(ProxyProperties proxyProperties) {
enforceConnectivityInternalPermission();
synchronized (mProxyLock) {
if (proxyProperties == mGlobalProxy) return;
if (proxyProperties != null && proxyProperties.equals(mGlobalProxy)) return;
@@ -3183,11 +3189,16 @@ public class ConnectivityService extends IConnectivityManager.Stub {
String host = "";
int port = 0;
String exclList = "";
if (proxyProperties != null && !TextUtils.isEmpty(proxyProperties.getHost())) {
String pacFileUrl = "";
if (proxyProperties != null && (!TextUtils.isEmpty(proxyProperties.getHost()) ||
!TextUtils.isEmpty(proxyProperties.getPacFileUrl()))) {
mGlobalProxy = new ProxyProperties(proxyProperties);
host = mGlobalProxy.getHost();
port = mGlobalProxy.getPort();
exclList = mGlobalProxy.getExclusionList();
if (proxyProperties.getPacFileUrl() != null) {
pacFileUrl = proxyProperties.getPacFileUrl();
}
} else {
mGlobalProxy = null;
}
@@ -3198,6 +3209,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, port);
Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
exclList);
Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC, pacFileUrl);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -3215,8 +3227,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
int port = Settings.Global.getInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, 0);
String exclList = Settings.Global.getString(res,
Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
if (!TextUtils.isEmpty(host)) {
ProxyProperties proxyProperties = new ProxyProperties(host, port, exclList);
String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC);
if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
ProxyProperties proxyProperties;
if (!TextUtils.isEmpty(pacFileUrl)) {
proxyProperties = new ProxyProperties(pacFileUrl);
} else {
proxyProperties = new ProxyProperties(host, port, exclList);
}
synchronized (mProxyLock) {
mGlobalProxy = proxyProperties;
}
@@ -3234,7 +3252,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
private void handleApplyDefaultProxy(ProxyProperties proxy) {
if (proxy != null && TextUtils.isEmpty(proxy.getHost())) {
if (proxy != null && TextUtils.isEmpty(proxy.getHost())
&& TextUtils.isEmpty(proxy.getPacFileUrl())) {
proxy = null;
}
synchronized (mProxyLock) {
@@ -3276,6 +3295,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private void sendProxyBroadcast(ProxyProperties proxy) {
if (proxy == null) proxy = new ProxyProperties("", 0, "");
mPacManager.setCurrentProxyScriptUrl(proxy);
if (DBG) log("sending Proxy Broadcast for " + proxy);
Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |