Merge "Global Proxy changes to proxy class."
This commit is contained in:
committed by
Android (Google) Code Review
commit
1da47a9401
@@ -16,32 +16,168 @@
|
||||
|
||||
package android.net;
|
||||
|
||||
import org.apache.http.HttpHost;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemProperties;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.apache.http.HttpHost;
|
||||
|
||||
/**
|
||||
* A convenience class for accessing the user and default proxy
|
||||
* settings.
|
||||
*/
|
||||
final public class Proxy {
|
||||
public final class Proxy {
|
||||
|
||||
// Set to true to enable extra debugging.
|
||||
static final private boolean DEBUG = false;
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
static final public String PROXY_CHANGE_ACTION =
|
||||
public static final String PROXY_CHANGE_ACTION =
|
||||
"android.intent.action.PROXY_CHANGE";
|
||||
|
||||
private static ReadWriteLock sProxyInfoLock = new ReentrantReadWriteLock();
|
||||
|
||||
private static SettingsObserver sGlobalProxyChangedObserver = null;
|
||||
|
||||
private static ProxySpec sGlobalProxySpec = null;
|
||||
|
||||
// Hostname / IP REGEX validation
|
||||
// Matches blank input, ips, and domain names
|
||||
private static final String NAME_IP_REGEX =
|
||||
"[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*";
|
||||
|
||||
private static final String HOSTNAME_REGEXP = "^$|^" + NAME_IP_REGEX + "$";
|
||||
|
||||
private static final Pattern HOSTNAME_PATTERN;
|
||||
|
||||
private static final String EXCLLIST_REGEXP = "$|^(.?" + NAME_IP_REGEX
|
||||
+ ")+(,(.?" + NAME_IP_REGEX + "))*$";
|
||||
|
||||
private static final Pattern EXCLLIST_PATTERN;
|
||||
|
||||
static {
|
||||
HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
|
||||
EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
|
||||
}
|
||||
|
||||
private static class ProxySpec {
|
||||
String[] exclusionList = null;
|
||||
InetSocketAddress proxyAddress = null;
|
||||
public ProxySpec() { };
|
||||
}
|
||||
|
||||
private static boolean isURLInExclusionListReadLocked(String url, String[] exclusionList) {
|
||||
if (exclusionList == null) {
|
||||
return false;
|
||||
}
|
||||
Uri u = Uri.parse(url);
|
||||
String urlDomain = u.getHost();
|
||||
// If the domain is defined as ".android.com" or "android.com", we wish to match
|
||||
// http://android.com as well as http://xxx.android.com , but not
|
||||
// http://myandroid.com . This code works out the logic.
|
||||
for (String excludedDomain : exclusionList) {
|
||||
String dotDomain = "." + excludedDomain;
|
||||
if (urlDomain.equals(excludedDomain)) {
|
||||
return true;
|
||||
}
|
||||
if (urlDomain.endsWith(dotDomain)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// No match
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String parseHost(String proxySpec) {
|
||||
int i = proxySpec.indexOf(':');
|
||||
if (i == -1) {
|
||||
if (DEBUG) {
|
||||
Assert.assertTrue(proxySpec.length() == 0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return proxySpec.substring(0, i);
|
||||
}
|
||||
|
||||
private static int parsePort(String proxySpec) {
|
||||
int i = proxySpec.indexOf(':');
|
||||
if (i == -1) {
|
||||
if (DEBUG) {
|
||||
Assert.assertTrue(proxySpec.length() == 0);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Assert.assertTrue(i < proxySpec.length());
|
||||
}
|
||||
return Integer.parseInt(proxySpec.substring(i+1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the proxy object to be used for the URL given as parameter.
|
||||
* @param ctx A Context used to get the settings for the proxy host.
|
||||
* @param url A URL to be accessed. Used to evaluate exclusion list.
|
||||
* @return Proxy (java.net) object containing the host name. If the
|
||||
* user did not set a hostname it returns the default host.
|
||||
* A null value means that no host is to be used.
|
||||
* {@hide}
|
||||
*/
|
||||
public static final java.net.Proxy getProxy(Context ctx, String url) {
|
||||
sProxyInfoLock.readLock().lock();
|
||||
try {
|
||||
if (sGlobalProxyChangedObserver == null) {
|
||||
registerContentObserversReadLocked(ctx);
|
||||
parseGlobalProxyInfoReadLocked(ctx);
|
||||
}
|
||||
if (sGlobalProxySpec != null) {
|
||||
// Proxy defined - Apply exclusion rules
|
||||
if (isURLInExclusionListReadLocked(url, sGlobalProxySpec.exclusionList)) {
|
||||
// Return no proxy
|
||||
return java.net.Proxy.NO_PROXY;
|
||||
}
|
||||
java.net.Proxy retProxy =
|
||||
new java.net.Proxy(java.net.Proxy.Type.HTTP, sGlobalProxySpec.proxyAddress);
|
||||
sProxyInfoLock.readLock().unlock();
|
||||
if (isLocalHost(url)) {
|
||||
return java.net.Proxy.NO_PROXY;
|
||||
}
|
||||
sProxyInfoLock.readLock().lock();
|
||||
return retProxy;
|
||||
} else {
|
||||
// If network is WiFi, return no proxy.
|
||||
// Otherwise, return the Mobile Operator proxy.
|
||||
if (!isNetworkWifi(ctx)) {
|
||||
java.net.Proxy retProxy = getDefaultProxy(url);
|
||||
sProxyInfoLock.readLock().unlock();
|
||||
if (isLocalHost(url)) {
|
||||
return java.net.Proxy.NO_PROXY;
|
||||
}
|
||||
sProxyInfoLock.readLock().lock();
|
||||
return retProxy;
|
||||
} else {
|
||||
return java.net.Proxy.NO_PROXY;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
sProxyInfoLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: deprecate this function
|
||||
/**
|
||||
* Return the proxy host set by the user.
|
||||
* @param ctx A Context used to get the settings for the proxy host.
|
||||
@@ -49,58 +185,53 @@ final public class Proxy {
|
||||
* name it returns the default host. A null value means that no
|
||||
* host is to be used.
|
||||
*/
|
||||
static final public String getHost(Context ctx) {
|
||||
ContentResolver contentResolver = ctx.getContentResolver();
|
||||
Assert.assertNotNull(contentResolver);
|
||||
String host = Settings.Secure.getString(
|
||||
contentResolver,
|
||||
Settings.Secure.HTTP_PROXY);
|
||||
if (host != null) {
|
||||
int i = host.indexOf(':');
|
||||
if (i == -1) {
|
||||
if (DEBUG) {
|
||||
Assert.assertTrue(host.length() == 0);
|
||||
}
|
||||
return null;
|
||||
public static final String getHost(Context ctx) {
|
||||
sProxyInfoLock.readLock().lock();
|
||||
try {
|
||||
if (sGlobalProxyChangedObserver == null) {
|
||||
registerContentObserversReadLocked(ctx);
|
||||
parseGlobalProxyInfoReadLocked(ctx);
|
||||
}
|
||||
return host.substring(0, i);
|
||||
if (sGlobalProxySpec != null) {
|
||||
InetSocketAddress sa = sGlobalProxySpec.proxyAddress;
|
||||
return sa.getHostName();
|
||||
}
|
||||
return getDefaultHost();
|
||||
} finally {
|
||||
sProxyInfoLock.readLock().unlock();
|
||||
}
|
||||
return getDefaultHost();
|
||||
}
|
||||
|
||||
// TODO: deprecate this function
|
||||
/**
|
||||
* Return the proxy port set by the user.
|
||||
* @param ctx A Context used to get the settings for the proxy port.
|
||||
* @return The port number to use or -1 if no proxy is to be used.
|
||||
*/
|
||||
static final public int getPort(Context ctx) {
|
||||
ContentResolver contentResolver = ctx.getContentResolver();
|
||||
Assert.assertNotNull(contentResolver);
|
||||
String host = Settings.Secure.getString(
|
||||
contentResolver,
|
||||
Settings.Secure.HTTP_PROXY);
|
||||
if (host != null) {
|
||||
int i = host.indexOf(':');
|
||||
if (i == -1) {
|
||||
if (DEBUG) {
|
||||
Assert.assertTrue(host.length() == 0);
|
||||
}
|
||||
return -1;
|
||||
public static final int getPort(Context ctx) {
|
||||
sProxyInfoLock.readLock().lock();
|
||||
try {
|
||||
if (sGlobalProxyChangedObserver == null) {
|
||||
registerContentObserversReadLocked(ctx);
|
||||
parseGlobalProxyInfoReadLocked(ctx);
|
||||
}
|
||||
if (DEBUG) {
|
||||
Assert.assertTrue(i < host.length());
|
||||
if (sGlobalProxySpec != null) {
|
||||
InetSocketAddress sa = sGlobalProxySpec.proxyAddress;
|
||||
return sa.getPort();
|
||||
}
|
||||
return Integer.parseInt(host.substring(i+1));
|
||||
return getDefaultPort();
|
||||
} finally {
|
||||
sProxyInfoLock.readLock().unlock();
|
||||
}
|
||||
return getDefaultPort();
|
||||
}
|
||||
|
||||
// TODO: deprecate this function
|
||||
/**
|
||||
* Return the default proxy host specified by the carrier.
|
||||
* @return String containing the host name or null if there is no proxy for
|
||||
* this carrier.
|
||||
*/
|
||||
static final public String getDefaultHost() {
|
||||
public static final String getDefaultHost() {
|
||||
String host = SystemProperties.get("net.gprs.http-proxy");
|
||||
if (host != null) {
|
||||
Uri u = Uri.parse(host);
|
||||
@@ -111,12 +242,13 @@ final public class Proxy {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: deprecate this function
|
||||
/**
|
||||
* Return the default proxy port specified by the carrier.
|
||||
* @return The port number to be used with the proxy host or -1 if there is
|
||||
* no proxy for this carrier.
|
||||
*/
|
||||
static final public int getDefaultPort() {
|
||||
public static final int getDefaultPort() {
|
||||
String host = SystemProperties.get("net.gprs.http-proxy");
|
||||
if (host != null) {
|
||||
Uri u = Uri.parse(host);
|
||||
@@ -126,6 +258,20 @@ final public class Proxy {
|
||||
}
|
||||
}
|
||||
|
||||
private static final java.net.Proxy getDefaultProxy(String url) {
|
||||
// TODO: This will go away when information is collected from ConnectivityManager...
|
||||
// There are broadcast of network proxies, so they are parse manually.
|
||||
String host = SystemProperties.get("net.gprs.http-proxy");
|
||||
if (host != null) {
|
||||
Uri u = Uri.parse(host);
|
||||
return new java.net.Proxy(java.net.Proxy.Type.HTTP,
|
||||
new InetSocketAddress(u.getHost(), u.getPort()));
|
||||
} else {
|
||||
return java.net.Proxy.NO_PROXY;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove this function / deprecate
|
||||
/**
|
||||
* Returns the preferred proxy to be used by clients. This is a wrapper
|
||||
* around {@link android.net.Proxy#getHost()}. Currently no proxy will
|
||||
@@ -138,26 +284,23 @@ final public class Proxy {
|
||||
* android.permission.ACCESS_NETWORK_STATE
|
||||
* @return The preferred proxy to be used by clients, or null if there
|
||||
* is no proxy.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
static final public HttpHost getPreferredHttpHost(Context context,
|
||||
public static final HttpHost getPreferredHttpHost(Context context,
|
||||
String url) {
|
||||
if (!isLocalHost(url) && !isNetworkWifi(context)) {
|
||||
final String proxyHost = Proxy.getHost(context);
|
||||
if (proxyHost != null) {
|
||||
return new HttpHost(proxyHost, Proxy.getPort(context), "http");
|
||||
}
|
||||
java.net.Proxy prefProxy = getProxy(context, url);
|
||||
if (prefProxy.equals(java.net.Proxy.NO_PROXY)) {
|
||||
return null;
|
||||
} else {
|
||||
InetSocketAddress sa = (InetSocketAddress)prefProxy.address();
|
||||
return new HttpHost(sa.getHostName(), sa.getPort(), "http");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static final private boolean isLocalHost(String url) {
|
||||
private static final boolean isLocalHost(String url) {
|
||||
if (url == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
final URI uri = URI.create(url);
|
||||
final String host = uri.getHost();
|
||||
@@ -174,15 +317,13 @@ final public class Proxy {
|
||||
} catch (IllegalArgumentException iex) {
|
||||
// Ignore (URI.create)
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static final private boolean isNetworkWifi(Context context) {
|
||||
private static final boolean isNetworkWifi(Context context) {
|
||||
if (context == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ConnectivityManager connectivity = (ConnectivityManager)
|
||||
context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (connectivity != null) {
|
||||
@@ -192,7 +333,120 @@ final public class Proxy {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
private static class SettingsObserver extends ContentObserver {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
SettingsObserver(Context ctx) {
|
||||
super(new Handler());
|
||||
mContext = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
sProxyInfoLock.readLock().lock();
|
||||
parseGlobalProxyInfoReadLocked(mContext);
|
||||
sProxyInfoLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static final void registerContentObserversReadLocked(Context ctx) {
|
||||
Uri uriGlobalProxy = Settings.Secure.getUriFor(Settings.Secure.HTTP_PROXY);
|
||||
Uri uriGlobalExclList =
|
||||
Settings.Secure.getUriFor(Settings.Secure.HTTP_PROXY_EXCLUSION_LIST);
|
||||
|
||||
// No lock upgrading (from read to write) allowed
|
||||
sProxyInfoLock.readLock().unlock();
|
||||
sProxyInfoLock.writeLock().lock();
|
||||
sGlobalProxyChangedObserver = new SettingsObserver(ctx);
|
||||
// Downgrading locks (from write to read) is allowed
|
||||
sProxyInfoLock.readLock().lock();
|
||||
sProxyInfoLock.writeLock().unlock();
|
||||
ctx.getContentResolver().registerContentObserver(uriGlobalProxy, false,
|
||||
sGlobalProxyChangedObserver);
|
||||
ctx.getContentResolver().registerContentObserver(uriGlobalExclList, false,
|
||||
sGlobalProxyChangedObserver);
|
||||
}
|
||||
|
||||
private static final void parseGlobalProxyInfoReadLocked(Context ctx) {
|
||||
ContentResolver contentResolver = ctx.getContentResolver();
|
||||
String proxyHost = Settings.Secure.getString(
|
||||
contentResolver,
|
||||
Settings.Secure.HTTP_PROXY);
|
||||
if (proxyHost == null) {
|
||||
return;
|
||||
}
|
||||
String exclusionListSpec = Settings.Secure.getString(
|
||||
contentResolver,
|
||||
Settings.Secure.HTTP_PROXY_EXCLUSION_LIST);
|
||||
String host = parseHost(proxyHost);
|
||||
int port = parsePort(proxyHost);
|
||||
if (proxyHost != null) {
|
||||
sGlobalProxySpec = new ProxySpec();
|
||||
sGlobalProxySpec.proxyAddress = new InetSocketAddress(host, port);
|
||||
if (exclusionListSpec != null) {
|
||||
String[] exclusionListEntries = exclusionListSpec.toLowerCase().split(",");
|
||||
String[] processedEntries = new String[exclusionListEntries.length];
|
||||
for (int i = 0; i < exclusionListEntries.length; i++) {
|
||||
String entry = exclusionListEntries[i].trim();
|
||||
if (entry.startsWith(".")) {
|
||||
entry = entry.substring(1);
|
||||
}
|
||||
processedEntries[i] = entry;
|
||||
}
|
||||
sProxyInfoLock.readLock().unlock();
|
||||
sProxyInfoLock.writeLock().lock();
|
||||
sGlobalProxySpec.exclusionList = processedEntries;
|
||||
} else {
|
||||
sProxyInfoLock.readLock().unlock();
|
||||
sProxyInfoLock.writeLock().lock();
|
||||
sGlobalProxySpec.exclusionList = null;
|
||||
}
|
||||
} else {
|
||||
sProxyInfoLock.readLock().unlock();
|
||||
sProxyInfoLock.writeLock().lock();
|
||||
sGlobalProxySpec = null;
|
||||
}
|
||||
sProxyInfoLock.readLock().lock();
|
||||
sProxyInfoLock.writeLock().unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate syntax of hostname, port and exclusion list entries
|
||||
* {@hide}
|
||||
*/
|
||||
public static void validate(String hostname, String port, String exclList) {
|
||||
Matcher match = HOSTNAME_PATTERN.matcher(hostname);
|
||||
Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList);
|
||||
|
||||
if (!match.matches()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
if (!listMatch.matches()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
if (hostname.length() > 0 && port.length() == 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
if (port.length() > 0) {
|
||||
if (hostname.length() == 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
int portVal = -1;
|
||||
try {
|
||||
portVal = Integer.parseInt(port);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (portVal <= 0 || portVal > 0xFFFF) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2321,10 +2321,25 @@ public final class Settings {
|
||||
public static final String DISABLED_SYSTEM_INPUT_METHODS = "disabled_system_input_methods";
|
||||
|
||||
/**
|
||||
* Host name and port for a user-selected proxy.
|
||||
* Host name and port for global proxy.
|
||||
*/
|
||||
public static final String HTTP_PROXY = "http_proxy";
|
||||
|
||||
/**
|
||||
* Exclusion list for global proxy. This string contains a list of comma-separated
|
||||
* domains where the global proxy does not apply. Domains should be listed in a comma-
|
||||
* separated list. Example of acceptable formats: ".domain1.com,my.domain2.com"
|
||||
* @hide
|
||||
*/
|
||||
public static final String HTTP_PROXY_EXCLUSION_LIST = "http_proxy_exclusion_list";
|
||||
|
||||
/**
|
||||
* Enables the UI setting to allow the user to specify the global HTTP proxy
|
||||
* and associated exclusion list.
|
||||
* @hide
|
||||
*/
|
||||
public static final String SET_GLOBAL_HTTP_PROXY = "set_global_http_proxy";
|
||||
|
||||
/**
|
||||
* Whether the package installer should allow installation of apps downloaded from
|
||||
* sources other than the Android Market (vending machine).
|
||||
|
||||
Reference in New Issue
Block a user