From c3c5f865a3513b94e39207734317060ae1d98b48 Mon Sep 17 00:00:00 2001 From: Robert Greenwalt Date: Mon, 11 Oct 2010 16:00:27 -0700 Subject: [PATCH] Notify all VMs when proxy changes. bug:2700664 Change-Id: I74cc6e0bd6e66847bf18f524ce851e3e9d2c4e87 --- .../java/android/net/ConnectivityManager.java | 35 +++++ .../android/net/IConnectivityManager.aidl | 7 + core/java/android/net/ProxyProperties.java | 142 ++++++++++++----- .../android/server/ConnectivityService.java | 143 +++++++++++++++++- 4 files changed, 285 insertions(+), 42 deletions(-) diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index dd9c8f09bd..ecfa2c1c59 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -624,4 +624,39 @@ public class ConnectivityManager } catch (RemoteException e) { } } + + /** + * @param proxyProperties The definition for the new global http proxy + * {@hide} + */ + public void setGlobalProxy(ProxyProperties p) { + try { + mService.setGlobalProxy(p); + } catch (RemoteException e) { + } + } + + /** + * @return proxyProperties for the current global proxy + * {@hide} + */ + public ProxyProperties getGlobalProxy() { + try { + return mService.getGlobalProxy(); + } catch (RemoteException e) { + return null; + } + } + + /** + * @return proxyProperties for the current proxy (global if set, network specific if not) + * {@hide} + */ + public ProxyProperties getProxy() { + try { + return mService.getProxy(); + } catch (RemoteException e) { + return null; + } + } } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 35054d6c40..70ab4f153e 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -18,6 +18,7 @@ package android.net; import android.net.LinkProperties; import android.net.NetworkInfo; +import android.net.ProxyProperties; import android.os.IBinder; /** @@ -85,4 +86,10 @@ interface IConnectivityManager void requestNetworkTransitionWakelock(in String forWhom); void reportInetCondition(int networkType, int percentage); + + ProxyProperties getGlobalProxy(); + + void setGlobalProxy(in ProxyProperties p); + + ProxyProperties getProxy(); } diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyProperties.java index 5fd0d89c72..cbe4445a3a 100644 --- a/core/java/android/net/ProxyProperties.java +++ b/core/java/android/net/ProxyProperties.java @@ -19,6 +19,8 @@ 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; @@ -30,44 +32,108 @@ import java.net.UnknownHostException; */ public class ProxyProperties implements Parcelable { - private InetSocketAddress mProxy; + private String mHost; + private int mPort; private String mExclusionList; + private String[] mParsedExclusionList; - public ProxyProperties() { + public ProxyProperties(String host, int port, String exclList) { + mHost = host; + mPort = port; + setExclusionList(exclList); + } + + private ProxyProperties(String host, int port, String exclList, String[] parsedExclList) { + mHost = host; + mPort = port; + mExclusionList = exclList; + mParsedExclusionList = parsedExclList; } // copy constructor instead of clone public ProxyProperties(ProxyProperties source) { if (source != null) { - mProxy = source.getSocketAddress(); - String exclusionList = source.getExclusionList(); - if (exclusionList != null) { - mExclusionList = new String(exclusionList); - } + mHost = source.getHost(); + mPort = source.getPort(); + mExclusionList = source.getExclusionList(); + mParsedExclusionList = source.mParsedExclusionList; } } public InetSocketAddress getSocketAddress() { - return mProxy; + InetSocketAddress inetSocketAddress = null; + try { + inetSocketAddress = new InetSocketAddress(mHost, mPort); + } catch (IllegalArgumentException e) { } + return inetSocketAddress; } - public void setSocketAddress(InetSocketAddress proxy) { - mProxy = proxy; + public String getHost() { + return mHost; } + public int getPort() { + return mPort; + } + + // comma separated public String getExclusionList() { return mExclusionList; } - public void setExclusionList(String exclusionList) { + // comma separated + private void setExclusionList(String exclusionList) { mExclusionList = exclusionList; + if (mExclusionList == null) { + mParsedExclusionList = new String[0]; + } else { + String splitExclusionList[] = exclusionList.toLowerCase().split(","); + mParsedExclusionList = new String[splitExclusionList.length * 2]; + for (int i = 0; i < splitExclusionList.length; i++) { + String s = splitExclusionList[i].trim(); + if (s.startsWith(".")) s = s.substring(1); + mParsedExclusionList[i*2] = s; + mParsedExclusionList[(i*2)+1] = "." + s; + } + } + } + + public boolean isExcluded(String url) { + if (TextUtils.isEmpty(url) || mParsedExclusionList == null || + mParsedExclusionList.length == 0) return false; + + Uri u = Uri.parse(url); + String urlDomain = u.getHost(); + if (urlDomain == null) return false; + for (int i = 0; i< mParsedExclusionList.length; i+=2) { + if (urlDomain.equals(mParsedExclusionList[i]) || + urlDomain.endsWith(mParsedExclusionList[i+1])) { + return true; + } + } + return false; + } + + public java.net.Proxy makeProxy() { + java.net.Proxy proxy = java.net.Proxy.NO_PROXY; + if (mHost != null) { + try { + InetSocketAddress inetSocketAddress = new InetSocketAddress(mHost, mPort); + proxy = new java.net.Proxy(java.net.Proxy.Type.HTTP, inetSocketAddress); + } catch (IllegalArgumentException e) { + } + } + return proxy; } @Override public String toString() { StringBuilder sb = new StringBuilder(); - if (mProxy != null) { - sb.append(mProxy.toString()); + if (mHost != null) { + sb.append("["); + sb.append(mHost); + sb.append("] "); + sb.append(Integer.toString(mPort)); if (mExclusionList != null) { sb.append(" xl=").append(mExclusionList); } @@ -75,6 +141,20 @@ public class ProxyProperties implements Parcelable { return sb.toString(); } + @Override + public boolean equals(Object o) { + if (!(o instanceof ProxyProperties)) return false; + ProxyProperties p = (ProxyProperties)o; + if (mExclusionList != null && !mExclusionList.equals(p.getExclusionList())) return false; + if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) { + return false; + } + if (mHost != null && p.mHost == null) return false; + if (mHost == null && p.mHost != null) return false; + if (mPort != p.mPort) return false; + return true; + } + /** * Implement the Parcelable interface * @hide @@ -88,27 +168,15 @@ public class ProxyProperties implements Parcelable { * @hide */ public void writeToParcel(Parcel dest, int flags) { - String host = null; - if (mProxy != null) { - try { - InetAddress addr = mProxy.getAddress(); - if (addr != null) { - host = addr.getHostAddress(); - } else { - /* Does not resolve when addr is null */ - host = mProxy.getHostName(); - } - } catch (Exception e) { } - } - - if (host != null) { + if (mHost != null) { dest.writeByte((byte)1); - dest.writeString(host); - dest.writeInt(mProxy.getPort()); + dest.writeString(mHost); + dest.writeInt(mPort); } else { dest.writeByte((byte)0); } dest.writeString(mExclusionList); + dest.writeStringArray(mParsedExclusionList); } /** @@ -118,16 +186,16 @@ public class ProxyProperties implements Parcelable { public static final Creator CREATOR = new Creator() { public ProxyProperties createFromParcel(Parcel in) { - ProxyProperties proxyProperties = new ProxyProperties(); + String host = null; + int port = 0; if (in.readByte() == 1) { - try { - String host = in.readString(); - int port = in.readInt(); - proxyProperties.setSocketAddress(InetSocketAddress.createUnresolved( - host, port)); - } catch (IllegalArgumentException e) { } + host = in.readString(); + port = in.readInt(); } - proxyProperties.setExclusionList(in.readString()); + String exclList = in.readString(); + String[] parsedExclList = in.readStringArray(); + ProxyProperties proxyProperties = + new ProxyProperties(host, port, exclList, parsedExclList); return proxyProperties; } diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index c18262e1d3..5c67da7f4d 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -22,6 +22,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.IConnectivityManager; import android.net.MobileDataStateTracker; @@ -29,8 +30,9 @@ import android.net.NetworkInfo; import android.net.LinkProperties; import android.net.NetworkStateTracker; import android.net.NetworkUtils; +import android.net.Proxy; +import android.net.ProxyProperties; import android.net.wifi.WifiStateTracker; -import android.net.NetworkUtils; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; @@ -55,13 +57,12 @@ import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; import java.util.GregorianCalendar; import java.util.List; -import java.net.InetAddress; -import java.net.UnknownHostException; /** * @hide @@ -179,6 +180,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = MAX_NETWORK_STATE_TRACKER_EVENT + 8; + /** + * used internally to reload global proxy settings + */ + private static final int EVENT_APPLY_GLOBAL_HTTP_PROXY = + MAX_NETWORK_STATE_TRACKER_EVENT + 9; + private Handler mHandler; // list of DeathRecipients used to make sure features are turned off when @@ -199,6 +206,14 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final int INET_CONDITION_LOG_MAX_SIZE = 15; private ArrayList mInetLog; + // track the current default http proxy - tell the world if we get a new one (real change) + private ProxyProperties mDefaultProxy = null; + // track the global proxy. + private ProxyProperties mGlobalProxy = null; + private final Object mGlobalProxyLock = new Object(); + + private SettingsObserver mSettingsObserver; + private static class NetworkAttributes { /** * Class for holding settings read from resources. @@ -412,6 +427,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (DBG) { mInetLog = new ArrayList(); } + + mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY); + mSettingsObserver.observe(mContext); } @@ -1303,6 +1321,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { mInitialBroadcast = null; } } + // load the global proxy at startup + mHandler.sendMessage(mHandler.obtainMessage(EVENT_APPLY_GLOBAL_HTTP_PROXY)); } private void handleConnect(NetworkInfo info) { @@ -1380,6 +1400,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (mNetTrackers[netType].getNetworkInfo().isConnected()) { if (mNetAttributes[netType].isDefault()) { + handleApplyDefaultProxy(netType); addDefaultRoute(mNetTrackers[netType]); } else { addPrivateDnsRoutes(mNetTrackers[netType]); @@ -1783,10 +1804,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { } break; case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED: - // TODO - make this handle ip/proxy/gateway/dns changes info = (NetworkInfo) msg.obj; type = info.getType(); - handleDnsConfigurationChange(type); + handleConnectivityChange(type); break; case EVENT_CLEAR_NET_TRANSITION_WAKELOCK: String causedBy = null; @@ -1838,6 +1858,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { handleSetMobileData(enabled); break; } + case EVENT_APPLY_GLOBAL_HTTP_PROXY: + { + handleDeprecatedGlobalHttpProxy(); + } } } } @@ -2037,4 +2061,113 @@ public class ConnectivityService extends IConnectivityManager.Stub { sendInetConditionBroadcast(networkInfo); return; } + + public synchronized ProxyProperties getProxy() { + if (mGlobalProxy != null) return mGlobalProxy; + if (mDefaultProxy != null) return mDefaultProxy; + return null; + } + + public void setGlobalProxy(ProxyProperties proxyProperties) { + enforceChangePermission(); + synchronized (mGlobalProxyLock) { + if (proxyProperties == mGlobalProxy) return; + if (proxyProperties != null && proxyProperties.equals(mGlobalProxy)) return; + if (mGlobalProxy != null && mGlobalProxy.equals(proxyProperties)) return; + + String host = ""; + int port = 0; + String exclList = ""; + if (proxyProperties != null && !TextUtils.isEmpty(proxyProperties.getHost())) { + mGlobalProxy = new ProxyProperties(proxyProperties); + host = mGlobalProxy.getHost(); + port = mGlobalProxy.getPort(); + exclList = mGlobalProxy.getExclusionList(); + } else { + mGlobalProxy = null; + } + ContentResolver res = mContext.getContentResolver(); + Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST, host); + Settings.Secure.putInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, port); + Settings.Secure.putString(res,Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST, + exclList); + } + + if (mGlobalProxy == null) { + proxyProperties = mDefaultProxy; + } + sendProxyBroadcast(proxyProperties); + } + + public ProxyProperties getGlobalProxy() { + synchronized (mGlobalProxyLock) { + return mGlobalProxy; + } + } + + private void handleApplyDefaultProxy(int type) { + // check if new default - push it out to all VM if so + ProxyProperties proxy = mNetTrackers[type].getLinkProperties().getHttpProxy(); + synchronized (this) { + if (mDefaultProxy != null && mDefaultProxy.equals(proxy)) return; + if (mDefaultProxy == proxy) return; + if (!TextUtils.isEmpty(proxy.getHost())) { + mDefaultProxy = proxy; + } else { + mDefaultProxy = null; + } + } + if (DBG) Slog.d(TAG, "changing default proxy to " + proxy); + if ((proxy == null && mGlobalProxy == null) || proxy.equals(mGlobalProxy)) return; + if (mGlobalProxy != null) return; + sendProxyBroadcast(proxy); + } + + private void handleDeprecatedGlobalHttpProxy() { + String proxy = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.HTTP_PROXY); + if (!TextUtils.isEmpty(proxy)) { + String data[] = proxy.split(":"); + String proxyHost = data[0]; + int proxyPort = 8080; + if (data.length > 1) { + try { + proxyPort = Integer.parseInt(data[1]); + } catch (NumberFormatException e) { + return; + } + } + ProxyProperties p = new ProxyProperties(data[0], proxyPort, ""); + setGlobalProxy(p); + } + } + + private void sendProxyBroadcast(ProxyProperties proxy) { + Slog.d(TAG, "sending Proxy Broadcast for " + proxy); + Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy); + mContext.sendBroadcast(intent); + } + + private static class SettingsObserver extends ContentObserver { + private int mWhat; + private Handler mHandler; + SettingsObserver(Handler handler, int what) { + super(handler); + mHandler = handler; + mWhat = what; + } + + void observe(Context context) { + ContentResolver resolver = context.getContentResolver(); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.HTTP_PROXY), false, this); + } + + @Override + public void onChange(boolean selfChange) { + mHandler.obtainMessage(mWhat).sendToTarget(); + } + } }