Notify all VMs when proxy changes.

bug:2700664
Change-Id: I74cc6e0bd6e66847bf18f524ce851e3e9d2c4e87
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3cead11..c0714e3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -42,6 +42,9 @@
 import android.database.sqlite.SQLiteDebug.DbStats;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.net.IConnectivityManager;
+import android.net.Proxy;
+import android.net.ProxyProperties;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
@@ -272,7 +275,7 @@
             super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky, token);
             this.intent = intent;
         }
-        
+
         Intent intent;
         ActivityInfo info;
         public String toString() {
@@ -592,6 +595,10 @@
             InetAddress.clearDnsCache();
         }
 
+        public void setHttpProxy(String host, String port, String exclList) {
+            Proxy.setHttpProxySystemProperty(host, port, exclList);
+        }
+
         public void processInBackground() {
             mH.removeMessages(H.GC_WHEN_IDLE);
             mH.sendMessage(mH.obtainMessage(H.GC_WHEN_IDLE));
@@ -3253,6 +3260,16 @@
             }
         }
 
+        /**
+         * Initialize the default http proxy in this process for the reasons we set the time zone.
+         */
+        IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+        IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+        try {
+            ProxyProperties proxyProperties = service.getProxy();
+            Proxy.setHttpProxySystemProperty(proxyProperties);
+        } catch (RemoteException e) {}
+
         if (data.instrumentationName != null) {
             ContextImpl appContext = new ContextImpl();
             appContext.init(data.info, null, this);
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index b19fb59..801c3f9 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -276,7 +276,7 @@
             requestThumbnail(b);
             return true;
         }
-        
+
         case SCHEDULE_CONFIGURATION_CHANGED_TRANSACTION:
         {
             data.enforceInterface(IApplicationThread.descriptor);
@@ -297,12 +297,21 @@
             return true;
         }
 
+        case SET_HTTP_PROXY_TRANSACTION: {
+            data.enforceInterface(IApplicationThread.descriptor);
+            final String proxy = data.readString();
+            final String port = data.readString();
+            final String exclList = data.readString();
+            setHttpProxy(proxy, port, exclList);
+            return true;
+        }
+
         case PROCESS_IN_BACKGROUND_TRANSACTION: {
             data.enforceInterface(IApplicationThread.descriptor);
             processInBackground();
             return true;
         }
-        
+
         case DUMP_SERVICE_TRANSACTION: {
             data.enforceInterface(IApplicationThread.descriptor);
             ParcelFileDescriptor fd = data.readFileDescriptor();
@@ -758,6 +767,16 @@
         data.recycle();
     }
 
+    public void setHttpProxy(String proxy, String port, String exclList) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeString(proxy);
+        data.writeString(port);
+        data.writeString(exclList);
+        mRemote.transact(SET_HTTP_PROXY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
+
     public void processInBackground() throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 830c702..eca84ef 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -88,6 +88,7 @@
     void scheduleConfigurationChanged(Configuration config) throws RemoteException;
     void updateTimeZone() throws RemoteException;
     void clearDnsCache() throws RemoteException;
+    void setHttpProxy(String proxy, String port, String exclList) throws RemoteException;
     void processInBackground() throws RemoteException;
     void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args)
             throws RemoteException;
@@ -148,4 +149,5 @@
     int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+35;
     int DUMP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+36;
     int CLEAR_DNS_CACHE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+37;
+    int SET_HTTP_PROXY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+38;
 }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index dd9c8f0..ecfa2c1 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -624,4 +624,39 @@
         } 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 35054d6..70ab4f1 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -18,6 +18,7 @@
 
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
+import android.net.ProxyProperties;
 import android.os.IBinder;
 
 /**
@@ -85,4 +86,10 @@
     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/Proxy.java b/core/java/android/net/Proxy.java
index 21c485e..3b9b9fe 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -16,9 +16,12 @@
 
 package android.net;
 
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
+import android.net.ProxyProperties;
 import android.os.Handler;
 import android.os.SystemProperties;
 import android.text.TextUtils;
@@ -55,17 +58,22 @@
 
     // Set to true to enable extra debugging.
     private static final boolean DEBUG = false;
+    private static final String TAG = "Proxy";
 
-    // Used to notify an app that's caching the default connection proxy
-    // that either the default connection or its proxy has changed
-    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;
+    /**
+     * Used to notify an app that's caching the default connection proxy
+     * that either the default connection or its proxy has changed.
+     * The intent will have the following extra value:</p>
+     * <ul>
+     *   <li><em>EXTRA_PROXY_INFO</em> - The ProxyProperties for the proxy
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
+    /** {@hide} **/
+    public static final String EXTRA_PROXY_INFO = "proxy";
 
     private static ConnectivityManager sConnectivityManager = null;
 
@@ -88,62 +96,6 @@
         EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
     }
 
-    // useful because it holds the processed exclusion list - don't want to reparse it each time
-    private static class ProxySpec {
-        String[] exclusionList;
-        InetSocketAddress address = null;
-        public ProxySpec() {
-            exclusionList = new String[0];
-        };
-    }
-
-    private static boolean isURLInExclusionList(String url, String[] exclusionList) {
-        if (url == 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.
@@ -154,34 +106,31 @@
      * {@hide}
      */
     public static final java.net.Proxy getProxy(Context ctx, String url) {
-        sProxyInfoLock.readLock().lock();
-        java.net.Proxy retval;
-        try {
-            if (sGlobalProxyChangedObserver == null) {
-                registerContentObserversReadLocked(ctx);
-                parseGlobalProxyInfoReadLocked(ctx);
+        String host = "";
+        if (url != null) {
+            URI uri = URI.create(url);
+            host = uri.getHost();
+        }
+
+        if (!isLocalHost(host)) {
+            if (sConnectivityManager == null) {
+                sConnectivityManager = (ConnectivityManager)ctx.getSystemService(
+                        Context.CONNECTIVITY_SERVICE);
             }
-            if (sGlobalProxySpec != null) {
-                // Proxy defined - Apply exclusion rules
-                if (isURLInExclusionList(url, sGlobalProxySpec.exclusionList)) {
-                    // Return no proxy
-                    retval = java.net.Proxy.NO_PROXY;
-                } else {
-                    retval =
-                        new java.net.Proxy(java.net.Proxy.Type.HTTP, sGlobalProxySpec.address);
+            if (sConnectivityManager == null) return java.net.Proxy.NO_PROXY;
+
+            ProxyProperties proxyProperties = sConnectivityManager.getProxy();
+
+            if (proxyProperties != null) {
+                if (!proxyProperties.isExcluded(host)) {
+                    return proxyProperties.makeProxy();
                 }
-            } else {
-                retval = getDefaultProxy(ctx, url);
             }
-        } finally {
-            sProxyInfoLock.readLock().unlock();
         }
-        if ((retval != java.net.Proxy.NO_PROXY) && (isLocalHost(url))) {
-            retval = java.net.Proxy.NO_PROXY;
-        }
-        return retval;
+        return java.net.Proxy.NO_PROXY;
     }
 
+
     // TODO: deprecate this function
     /**
      * Return the proxy host set by the user.
@@ -236,35 +185,6 @@
         return -1;
     }
 
-    // TODO - cache the details for each network so we don't have to fetch and parse
-    // on each request
-    private static final java.net.Proxy getDefaultProxy(Context context, String url) {
-        if (sConnectivityManager == null) {
-            sConnectivityManager = (ConnectivityManager)context.getSystemService(
-                    Context.CONNECTIVITY_SERVICE);
-        }
-        if (sConnectivityManager == null) return java.net.Proxy.NO_PROXY;
-
-        LinkProperties linkProperties = sConnectivityManager.getActiveLinkProperties();
-
-        if (linkProperties != null) {
-            ProxyProperties proxyProperties = linkProperties.getHttpProxy();
-
-            if (proxyProperties != null) {
-                String exclusionList = proxyProperties.getExclusionList();
-                SocketAddress socketAddr = proxyProperties.getSocketAddress();
-                if (socketAddr != null) {
-                    String[] parsedExclusionArray =
-                            parsedExclusionArray = parseExclusionList(exclusionList);
-                    if (!isURLInExclusionList(url, parsedExclusionArray)) {
-                        return new java.net.Proxy(java.net.Proxy.Type.HTTP, socketAddr);
-                    }
-                }
-            }
-        }
-        return java.net.Proxy.NO_PROXY;
-    }
-
     // TODO: remove this function / deprecate
     /**
      * Returns the preferred proxy to be used by clients. This is a wrapper
@@ -291,13 +211,11 @@
         }
     }
 
-    private static final boolean isLocalHost(String url) {
-        if (url == null) {
+    private static final boolean isLocalHost(String host) {
+        if (host == null) {
             return false;
         }
         try {
-            final URI uri = URI.create(url);
-            final String host = uri.getHost();
             if (host != null) {
                 if (host.equalsIgnoreCase("localhost")) {
                     return true;
@@ -317,92 +235,6 @@
         return false;
     }
 
-    private static class SettingsObserver extends ContentObserver {
-
-        private Context mContext;
-
-        SettingsObserver(Context ctx) {
-            super(new Handler(ctx.getMainLooper()));
-            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();
-        try {
-            sGlobalProxyChangedObserver = new SettingsObserver(ctx);
-        } finally {
-            // 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 (TextUtils.isEmpty(proxyHost)) {
-            // Clear signal
-            sProxyInfoLock.readLock().unlock();
-            sProxyInfoLock.writeLock().lock();
-            sGlobalProxySpec = null;
-            sProxyInfoLock.readLock().lock();
-            sProxyInfoLock.writeLock().unlock();
-            return;
-        }
-        String exclusionListSpec = Settings.Secure.getString(
-                contentResolver,
-                Settings.Secure.HTTP_PROXY_EXCLUSION_LIST);
-        String host = parseHost(proxyHost);
-        int port = parsePort(proxyHost);
-        ProxySpec tmpProxySpec = null;
-        if (proxyHost != null) {
-            tmpProxySpec = new ProxySpec();
-            tmpProxySpec.address = new InetSocketAddress(host, port);
-            tmpProxySpec.exclusionList = parseExclusionList(exclusionListSpec);
-        }
-        sProxyInfoLock.readLock().unlock();
-        sProxyInfoLock.writeLock().lock();
-        sGlobalProxySpec = tmpProxySpec;
-        sProxyInfoLock.readLock().lock();
-        sProxyInfoLock.writeLock().unlock();
-    }
-
-    private static String[] parseExclusionList(String exclusionList) {
-        String[] processedArray = new String[0];
-        if (!TextUtils.isEmpty(exclusionList)) {
-            String[] exclusionListArray = exclusionList.toLowerCase().split(",");
-            processedArray = new String[exclusionListArray.length];
-            for (int i = 0; i < exclusionListArray.length; i++) {
-                String entry = exclusionListArray[i].trim();
-                if (entry.startsWith(".")) {
-                    entry = entry.substring(1);
-                }
-                processedArray[i] = entry;
-            }
-        }
-        return processedArray;
-    }
-
     /**
      * Validate syntax of hostname, port and exclusion list entries
      * {@hide}
@@ -480,4 +312,44 @@
                 new SchemeRegistry(), ProxySelector.getDefault(), context);
         return ret;
     }
+
+    /** @hide */
+    public static final void setHttpProxySystemProperty(ProxyProperties p) {
+        String host = null;
+        String port = null;
+        String exclList = null;
+        if (p != null) {
+            host = p.getHost();
+            port = Integer.toString(p.getPort());
+            exclList = p.getExclusionList();
+        }
+        setHttpProxySystemProperty(host, port, exclList);
+    }
+
+    /** @hide */
+    public static final void setHttpProxySystemProperty(String host, String port, String exclList) {
+        if (exclList != null) exclList = exclList.replace(",", "|");
+        if (false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList);
+        if (host != null) {
+            System.setProperty("http.proxyHost", host);
+            System.setProperty("https.proxyHost", host);
+        } else {
+            System.clearProperty("http.proxyHost");
+            System.clearProperty("https.proxyHost");
+        }
+        if (port != null) {
+            System.setProperty("http.proxyPort", port);
+            System.setProperty("https.proxyPort", port);
+        } else {
+            System.clearProperty("http.proxyPort");
+            System.clearProperty("https.proxyPort");
+        }
+        if (exclList != null) {
+            System.setProperty("http.nonProxyHosts", exclList);
+            System.setProperty("https.nonProxyHosts", exclList);
+        } else {
+            System.clearProperty("http.nonProxyHosts");
+            System.clearProperty("https.nonProxyHosts");
+        }
+    }
 }
diff --git a/core/java/android/net/ProxyProperties.aidl b/core/java/android/net/ProxyProperties.aidl
new file mode 100644
index 0000000..02ea15d
--- /dev/null
+++ b/core/java/android/net/ProxyProperties.aidl
@@ -0,0 +1,21 @@
+/*
+**
+** Copyright (C) 2010 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.net;
+
+parcelable ProxyProperties;
+
diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyProperties.java
index 5fd0d89..cbe4445 100644
--- a/core/java/android/net/ProxyProperties.java
+++ b/core/java/android/net/ProxyProperties.java
@@ -19,6 +19,8 @@
 
 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 @@
  */
 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 @@
         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 @@
      * @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 static final Creator<ProxyProperties> CREATOR =
         new Creator<ProxyProperties>() {
             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/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1fe2c5a..7075774 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2424,17 +2424,32 @@
         public static final String DISABLED_SYSTEM_INPUT_METHODS = "disabled_system_input_methods";
 
         /**
-         * Host name and port for global proxy.
+         * Host name and port for global http proxy.  Uses ':' seperator for between host and port
+         * TODO - deprecate in favor of global_http_proxy_host, etc
          */
         public static final String HTTP_PROXY = "http_proxy";
 
         /**
+         * Host name for global http proxy.  Set via ConnectivityManager.
+         * @hide
+         */
+        public static final String GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host";
+
+        /**
+         * Integer host port for global http proxy.  Set via ConnectivityManager.
+         * @hide
+         */
+        public static final String GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port";
+
+        /**
          * 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"
+         * Use ConnectivityManager to set/get.
          * @hide
          */
-        public static final String HTTP_PROXY_EXCLUSION_LIST = "http_proxy_exclusion_list";
+        public static final String GLOBAL_HTTP_PROXY_EXCLUSION_LIST =
+                "global_http_proxy_exclusion_list";
 
         /**
          * Enables the UI setting to allow the user to specify the global HTTP proxy
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d02ca64..ddc63dd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -92,6 +92,7 @@
     <protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" />
     <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
     <protected-broadcast android:name="android.intent.action.CLEAR_DNS_CACHE" />
+    <protected-broadcast android:name="android.intent.action.PROXY_CHANGE" />
 
     <!-- ====================================== -->
     <!-- Permissions for things that cost money -->
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index c18262e..5c67da7 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -22,6 +22,7 @@
 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.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.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 @@
     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 @@
     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 @@
         if (DBG) {
             mInetLog = new ArrayList();
         }
+
+        mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
+        mSettingsObserver.observe(mContext);
     }
 
 
@@ -1303,6 +1321,8 @@
                 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 @@
 
         if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
             if (mNetAttributes[netType].isDefault()) {
+                handleApplyDefaultProxy(netType);
                 addDefaultRoute(mNetTrackers[netType]);
             } else {
                 addPrivateDnsRoutes(mNetTrackers[netType]);
@@ -1783,10 +1804,9 @@
                     }
                     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 @@
                     handleSetMobileData(enabled);
                     break;
                 }
+                case EVENT_APPLY_GLOBAL_HTTP_PROXY:
+                {
+                    handleDeprecatedGlobalHttpProxy();
+                }
             }
         }
     }
@@ -2037,4 +2061,113 @@
         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();
+        }
+    }
 }
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 3dcad38..2b43b013 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -1757,10 +1757,19 @@
         }
         // Remove white spaces
         proxySpec = proxySpec.trim();
+        String data[] = proxySpec.split(":");
+        int proxyPort = 8080;
+        if (data.length > 1) {
+            try {
+                proxyPort = Integer.parseInt(data[1]);
+            } catch (NumberFormatException e) {}
+        }
         exclusionList = exclusionList.trim();
         ContentResolver res = mContext.getContentResolver();
-        Settings.Secure.putString(res, Settings.Secure.HTTP_PROXY, proxySpec);
-        Settings.Secure.putString(res, Settings.Secure.HTTP_PROXY_EXCLUSION_LIST, exclusionList);
+        Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST, data[0]);
+        Settings.Secure.putInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, proxyPort);
+        Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
+                exclusionList);
     }
 
     @Override
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 1ec8a22..e815524 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -76,6 +76,8 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.net.Proxy;
+import android.net.ProxyProperties;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -127,6 +129,7 @@
 import java.io.PrintWriter;
 import java.lang.IllegalStateException;
 import java.lang.ref.WeakReference;
+import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -960,6 +963,7 @@
     static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26;
     static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27;
     static final int CLEAR_DNS_CACHE = 28;
+    static final int UPDATE_HTTP_PROXY = 29;
 
     AlertDialog mUidAlert;
 
@@ -1125,6 +1129,30 @@
                     }
                 }
             } break;
+            case UPDATE_HTTP_PROXY: {
+                ProxyProperties proxy = (ProxyProperties)msg.obj;
+                String host = "";
+                String port = "";
+                String exclList = "";
+                if (proxy != null) {
+                    host = proxy.getHost();
+                    port = Integer.toString(proxy.getPort());
+                    exclList = proxy.getExclusionList();
+                }
+                synchronized (ActivityManagerService.this) {
+                    for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
+                        ProcessRecord r = mLruProcesses.get(i);
+                        if (r.thread != null) {
+                            try {
+                                r.thread.setHttpProxy(host, port, exclList);
+                            } catch (RemoteException ex) {
+                                Slog.w(TAG, "Failed to update http proxy for: " +
+                                        r.info.processName);
+                            }
+                        }
+                    }
+                }
+            } break;
             case SHOW_UID_ERROR_MSG: {
                 // XXX This is a temporary dialog, no need to localize.
                 AlertDialog d = new BaseErrorDialog(mContext);
@@ -10402,6 +10430,11 @@
             mHandler.sendEmptyMessage(CLEAR_DNS_CACHE);
         }
 
+        if (Proxy.PROXY_CHANGE_ACTION.equals(intent.getAction())) {
+            ProxyProperties proxy = intent.getParcelableExtra("proxy");
+            mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY, proxy));
+        }
+
         /*
          * Prevent non-system code (defined here to be non-persistent
          * processes) from sending protected broadcasts.
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 70328f7..06c7c1b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -1007,14 +1007,9 @@
             ApnSetting apn = mPendingDataConnection.getApn();
             if (apn.proxy != null && apn.proxy.length() != 0) {
                 try {
-                    ProxyProperties proxy = new ProxyProperties();
-                    proxy.setSocketAddress(new InetSocketAddress(InetAddress.getByName(apn.proxy),
-                            Integer.parseInt(apn.port)));
+                    ProxyProperties proxy = new ProxyProperties(apn.proxy,
+                            Integer.parseInt(apn.port), null);
                     mLinkProperties.setHttpProxy(proxy);
-                } catch (UnknownHostException e) {
-                    loge("UnknownHostException making ProxyProperties: " + e);
-                } catch (SecurityException e) {
-                    loge("SecurityException making ProxyProperties: " + e);
                 } catch (NumberFormatException e) {
                     loge("NumberFormatException making ProxyProperties (" + apn.port +
                             "): " + e);
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 04b3891..24f6f31 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -595,9 +595,9 @@
                                 out.writeUTF(PROXY_SETTINGS_KEY);
                                 out.writeUTF(config.proxySettings.toString());
                                 out.writeUTF(PROXY_HOST_KEY);
-                                out.writeUTF(proxyProperties.getSocketAddress().getHostName());
+                                out.writeUTF(proxyProperties.getHost());
                                 out.writeUTF(PROXY_PORT_KEY);
-                                out.writeInt(proxyProperties.getSocketAddress().getPort());
+                                out.writeInt(proxyProperties.getPort());
                                 out.writeUTF(EXCLUSION_LIST_KEY);
                                 out.writeUTF(exclusionList);
                                 writeToFile = true;
@@ -716,10 +716,8 @@
                             switch (proxySettings) {
                                 case STATIC:
                                     config.proxySettings = proxySettings;
-                                    ProxyProperties proxyProperties = new ProxyProperties();
-                                    proxyProperties.setSocketAddress(
-                                            new InetSocketAddress(proxyHost, proxyPort));
-                                    proxyProperties.setExclusionList(exclusionList);
+                                    ProxyProperties proxyProperties =
+                                        new ProxyProperties(proxyHost, proxyPort, exclusionList);
                                     linkProperties.setHttpProxy(proxyProperties);
                                     break;
                                 case NONE:
@@ -1012,42 +1010,13 @@
 
         switch (newConfig.proxySettings) {
             case STATIC:
-                InetSocketAddress newSockAddr = null;
-                String newExclusionList = null;
-                InetSocketAddress currentSockAddr = null;
-                String currentExclusionList = null;
-
                 ProxyProperties newHttpProxy = newConfig.linkProperties.getHttpProxy();
-                if (newHttpProxy != null) {
-                    newSockAddr = newHttpProxy.getSocketAddress();
-                    newExclusionList = newHttpProxy.getExclusionList();
-                }
-
                 ProxyProperties currentHttpProxy = currentConfig.linkProperties.getHttpProxy();
-                if (currentHttpProxy != null) {
-                    currentSockAddr =  currentHttpProxy.getSocketAddress();
-                    currentExclusionList = currentHttpProxy.getExclusionList();
-                }
 
-                boolean socketAddressDiffers = false;
-                boolean exclusionListDiffers = false;
-
-                if (newSockAddr != null && currentSockAddr != null ) {
-                    socketAddressDiffers = !currentSockAddr.equals(newSockAddr);
-                } else if (newSockAddr != null || currentSockAddr != null) {
-                    socketAddressDiffers = true;
-                }
-
-                if (newExclusionList != null && currentExclusionList != null) {
-                    exclusionListDiffers = !currentExclusionList.equals(newExclusionList);
-                } else if (newExclusionList != null || currentExclusionList != null) {
-                    exclusionListDiffers = true;
-                }
-
-                if ((currentConfig.proxySettings != newConfig.proxySettings) ||
-                        socketAddressDiffers ||
-                        exclusionListDiffers) {
-                    proxyChanged = true;
+                if (newHttpProxy != null) {
+                    proxyChanged = !newHttpProxy.equals(currentHttpProxy);
+                } else {
+                    proxyChanged = (currentHttpProxy != null);
                 }
                 break;
             case NONE: