resolved conflicts for merge of 26ecc31f to honeycomb-plus-aosp

Change-Id: I23f42e247884f0c9d5ae4d3466213592dd3433d9
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 494e922..5b8076e 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -164,15 +164,19 @@
     public static final String EXTRA_ERRORED_TETHER = "erroredArray";
 
     /**
+     * The absence of APN..
+     * @hide
+     */
+    public static final int TYPE_NONE        = -1;
+
+    /**
      * The Default Mobile data connection.  When active, all data traffic
-     * will use this connection by default.  Should not coexist with other
-     * default connections.
+     * will use this connection by default.
      */
     public static final int TYPE_MOBILE      = 0;
     /**
      * The Default WIFI data connection.  When active, all data traffic
-     * will use this connection by default.  Should not coexist with other
-     * default connections.
+     * will use this connection by default.
      */
     public static final int TYPE_WIFI        = 1;
     /**
@@ -208,23 +212,47 @@
     public static final int TYPE_MOBILE_HIPRI = 5;
     /**
      * The Default WiMAX data connection.  When active, all data traffic
-     * will use this connection by default.  Should not coexist with other
-     * default connections.
+     * will use this connection by default.
      */
     public static final int TYPE_WIMAX       = 6;
+
     /**
-     * Bluetooth data connection.
-     * @hide
+     * The Default Bluetooth data connection. When active, all data traffic
+     * will use this connection by default.
      */
     public static final int TYPE_BLUETOOTH   = 7;
+
     /** {@hide} */
     public static final int TYPE_DUMMY       = 8;
-    /** {@hide} */
+
+    /**
+     * The Default Ethernet data connection.  When active, all data traffic
+     * will use this connection by default.
+     */
     public static final int TYPE_ETHERNET    = 9;
-    /** {@hide} TODO: Need to adjust this for WiMAX. */
-    public static final int MAX_RADIO_TYPE   = TYPE_ETHERNET;
-    /** {@hide} TODO: Need to adjust this for WiMAX. */
-    public static final int MAX_NETWORK_TYPE = TYPE_ETHERNET;
+    /**
+     * Over the air Adminstration.
+     * {@hide}
+     */
+    public static final int TYPE_MOBILE_FOTA = 10;
+
+    /**
+     * IP Multimedia Subsystem
+     * {@hide}
+     */
+    public static final int TYPE_MOBILE_IMS  = 11;
+
+    /**
+     * Carrier Branded Services
+     * {@hide}
+     */
+    public static final int TYPE_MOBILE_CBS  = 12;
+
+    /** {@hide} */
+    public static final int MAX_RADIO_TYPE   = TYPE_MOBILE_CBS;
+
+    /** {@hide} */
+    public static final int MAX_NETWORK_TYPE = TYPE_MOBILE_CBS;
 
     public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI;
 
@@ -273,6 +301,24 @@
         }
     }
 
+    /** @hide */
+    public LinkProperties getActiveLinkProperties() {
+        try {
+            return mService.getActiveLinkProperties();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /** @hide */
+    public LinkProperties getLinkProperties(int networkType) {
+        try {
+            return mService.getLinkProperties(networkType);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
     /** {@hide} */
     public boolean setRadios(boolean turnOn) {
         try {
@@ -376,14 +422,14 @@
      * <p>
      * All applications that have background services that use the network
      * should listen to {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}.
-     * 
+     *
      * @return Whether background data usage is allowed.
      */
     public boolean getBackgroundDataSetting() {
         try {
             return mService.getBackgroundDataSetting();
         } catch (RemoteException e) {
-            // Err on the side of safety 
+            // Err on the side of safety
             return false;
         }
     }
@@ -541,6 +587,17 @@
         }
     }
 
+    /**
+     * {@hide}
+     */
+    public String[] getTetherableBluetoothRegexs() {
+        try {
+            return mService.getTetherableBluetoothRegexs();
+        } catch (RemoteException e) {
+            return new String[0];
+        }
+    }
+
     /** {@hide} */
     public static final int TETHER_ERROR_NO_ERROR           = 0;
     /** {@hide} */
@@ -579,6 +636,21 @@
     }
 
     /**
+     * Ensure the device stays awake until we connect with the next network
+     * @param forWhome The name of the network going down for logging purposes
+     * @return {@code true} on success, {@code false} on failure
+     * {@hide}
+     */
+    public boolean requestNetworkTransitionWakelock(String forWhom) {
+        try {
+            mService.requestNetworkTransitionWakelock(forWhom);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * @param networkType The type of network you want to report on
      * @param percentage The quality of the connection 0 is bad, 100 is good
      * {@hide}
@@ -589,4 +661,51 @@
         } 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;
+        }
+    }
+
+    /**
+     * @param networkType The network who's dependence has changed
+     * @param met Boolean - true if network use is ok, false if not
+     * {@hide}
+     */
+    public void setDataDependency(int networkType, boolean met) {
+        try {
+            mService.setDataDependency(networkType, met);
+        } catch (RemoteException e) {
+        }
+    }
 }
diff --git a/core/java/android/net/DhcpInfo.java b/core/java/android/net/DhcpInfo.java
index 1178bec..e2660e4 100644
--- a/core/java/android/net/DhcpInfo.java
+++ b/core/java/android/net/DhcpInfo.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcelable;
 import android.os.Parcel;
+import java.net.InetAddress;
 
 /**
  * A simple object for retrieving the results of a DHCP request.
@@ -37,6 +38,19 @@
         super();
     }
 
+    /** copy constructor {@hide} */
+    public DhcpInfo(DhcpInfo source) {
+        if (source != null) {
+            ipAddress = source.ipAddress;
+            gateway = source.gateway;
+            netmask = source.netmask;
+            dns1 = source.dns1;
+            dns2 = source.dns2;
+            serverAddress = source.serverAddress;
+            leaseDuration = source.leaseDuration;
+        }
+    }
+
     public String toString() {
         StringBuffer str = new StringBuffer();
 
@@ -52,10 +66,7 @@
     }
 
     private static void putAddress(StringBuffer buf, int addr) {
-        buf.append(addr  & 0xff).append('.').
-            append((addr >>>= 8) & 0xff).append('.').
-            append((addr >>>= 8) & 0xff).append('.').
-            append((addr >>>= 8) & 0xff);
+        buf.append(NetworkUtils.intToInetAddress(addr).getHostAddress());
     }
 
     /** Implement the Parcelable interface {@hide} */
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index afccdd9..8be492c 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -1,22 +1,24 @@
 /**
  * Copyright (c) 2008, 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 
+ * 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 
+ *     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 
+ * 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;
 
+import android.net.LinkProperties;
 import android.net.NetworkInfo;
+import android.net.ProxyProperties;
 import android.os.IBinder;
 
 /**
@@ -36,6 +38,10 @@
 
     NetworkInfo[] getAllNetworkInfo();
 
+    LinkProperties getActiveLinkProperties();
+
+    LinkProperties getLinkProperties(int networkType);
+
     boolean setRadios(boolean onOff);
 
     boolean setRadio(int networkType, boolean turnOn);
@@ -75,5 +81,17 @@
 
     String[] getTetherableWifiRegexs();
 
+    String[] getTetherableBluetoothRegexs();
+
+    void requestNetworkTransitionWakelock(in String forWhom);
+
     void reportInetCondition(int networkType, int percentage);
+
+    ProxyProperties getGlobalProxy();
+
+    void setGlobalProxy(in ProxyProperties p);
+
+    ProxyProperties getProxy();
+
+    void setDataDependency(int networkType, boolean met);
 }
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
new file mode 100644
index 0000000..f6a114c
--- /dev/null
+++ b/core/java/android/net/LinkAddress.java
@@ -0,0 +1,147 @@
+/*
+ * 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Identifies an address of a network link
+ * @hide
+ */
+public class LinkAddress implements Parcelable {
+    /**
+     * IPv4 or IPv6 address.
+     */
+    private final InetAddress address;
+
+    /**
+     * Network prefix length
+     */
+    private final int prefixLength;
+
+    public LinkAddress(InetAddress address, int prefixLength) {
+        if (address == null || prefixLength < 0 ||
+                ((address instanceof Inet4Address) && prefixLength > 32) ||
+                (prefixLength > 128)) {
+            throw new IllegalArgumentException("Bad LinkAddress params " + address +
+                    prefixLength);
+        }
+        this.address = address;
+        this.prefixLength = prefixLength;
+    }
+
+    public LinkAddress(InterfaceAddress interfaceAddress) {
+        this.address = interfaceAddress.getAddress();
+        this.prefixLength = interfaceAddress.getNetworkPrefixLength();
+    }
+
+    @Override
+    public String toString() {
+        return (address == null ? "" : (address.getHostAddress() + "/" + prefixLength));
+    }
+
+    /**
+     * Compares this {@code LinkAddress} instance against the specified address
+     * in {@code obj}. Two addresses are equal if their InetAddress and prefixLength
+     * are equal
+     *
+     * @param obj the object to be tested for equality.
+     * @return {@code true} if both objects are equal, {@code false} otherwise.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof LinkAddress)) {
+            return false;
+        }
+        LinkAddress linkAddress = (LinkAddress) obj;
+        return this.address.equals(linkAddress.address) &&
+            this.prefixLength == linkAddress.prefixLength;
+    }
+
+    @Override
+    /*
+     * generate hashcode based on significant fields
+     */
+    public int hashCode() {
+        return ((null == address) ? 0 : address.hashCode()) + prefixLength;
+    }
+
+    /**
+     * Returns the InetAddress for this address.
+     */
+    public InetAddress getAddress() {
+        return address;
+    }
+
+    /**
+     * Get network prefix length
+     */
+    public int getNetworkPrefixLength() {
+        return prefixLength;
+    }
+
+    /**
+     * Implement the Parcelable interface
+     * @hide
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        if (address != null) {
+            dest.writeByte((byte)1);
+            dest.writeByteArray(address.getAddress());
+            dest.writeInt(prefixLength);
+        } else {
+            dest.writeByte((byte)0);
+        }
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public static final Creator<LinkAddress> CREATOR =
+        new Creator<LinkAddress>() {
+            public LinkAddress createFromParcel(Parcel in) {
+                InetAddress address = null;
+                int prefixLength = 0;
+                if (in.readByte() == 1) {
+                    try {
+                        address = InetAddress.getByAddress(in.createByteArray());
+                        prefixLength = in.readInt();
+                    } catch (UnknownHostException e) { }
+                }
+                return new LinkAddress(address, prefixLength);
+            }
+
+            public LinkAddress[] newArray(int size) {
+                return new LinkAddress[size];
+            }
+        };
+}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
new file mode 100644
index 0000000..132f3ba
--- /dev/null
+++ b/core/java/android/net/LinkProperties.java
@@ -0,0 +1,435 @@
+/*
+ * 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;
+
+import android.net.ProxyProperties;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Describes the properties of a network link.
+ *
+ * A link represents a connection to a network.
+ * It may have multiple addresses and multiple gateways,
+ * multiple dns servers but only one http proxy.
+ *
+ * Because it's a single network, the dns's
+ * are interchangeable and don't need associating with
+ * particular addresses.  The gateways similarly don't
+ * need associating with particular addresses.
+ *
+ * A dual stack interface works fine in this model:
+ * each address has it's own prefix length to describe
+ * the local network.  The dns servers all return
+ * both v4 addresses and v6 addresses regardless of the
+ * address family of the server itself (rfc4213) and we
+ * don't care which is used.  The gateways will be
+ * selected based on the destination address and the
+ * source address has no relavence.
+ * @hide
+ */
+public class LinkProperties implements Parcelable {
+
+    String mIfaceName;
+    private Collection<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
+    private Collection<InetAddress> mDnses = new ArrayList<InetAddress>();
+    private Collection<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
+    private ProxyProperties mHttpProxy;
+
+    public static class CompareResult<T> {
+        public Collection<T> removed = new ArrayList<T>();
+        public Collection<T> added = new ArrayList<T>();
+
+        @Override
+        public String toString() {
+            String retVal = "removed=[";
+            for (T addr : removed) retVal += addr.toString() + ",";
+            retVal += "] added=[";
+            for (T addr : added) retVal += addr.toString() + ",";
+            retVal += "]";
+            return retVal;
+        }
+    }
+
+    public LinkProperties() {
+        clear();
+    }
+
+    // copy constructor instead of clone
+    public LinkProperties(LinkProperties source) {
+        if (source != null) {
+            mIfaceName = source.getInterfaceName();
+            mLinkAddresses = source.getLinkAddresses();
+            mDnses = source.getDnses();
+            mRoutes = source.getRoutes();
+            mHttpProxy = (source.getHttpProxy() == null)  ?
+                null : new ProxyProperties(source.getHttpProxy());
+        }
+    }
+
+    public void setInterfaceName(String iface) {
+        mIfaceName = iface;
+    }
+
+    public String getInterfaceName() {
+        return mIfaceName;
+    }
+
+    public Collection<InetAddress> getAddresses() {
+        Collection<InetAddress> addresses = new ArrayList<InetAddress>();
+        for (LinkAddress linkAddress : mLinkAddresses) {
+            addresses.add(linkAddress.getAddress());
+        }
+        return Collections.unmodifiableCollection(addresses);
+    }
+
+    public void addLinkAddress(LinkAddress address) {
+        if (address != null) mLinkAddresses.add(address);
+    }
+
+    public Collection<LinkAddress> getLinkAddresses() {
+        return Collections.unmodifiableCollection(mLinkAddresses);
+    }
+
+    public void addDns(InetAddress dns) {
+        if (dns != null) mDnses.add(dns);
+    }
+
+    public Collection<InetAddress> getDnses() {
+        return Collections.unmodifiableCollection(mDnses);
+    }
+
+    public void addRoute(RouteInfo route) {
+        if (route != null) mRoutes.add(route);
+    }
+    public Collection<RouteInfo> getRoutes() {
+        return Collections.unmodifiableCollection(mRoutes);
+    }
+
+    public void setHttpProxy(ProxyProperties proxy) {
+        mHttpProxy = proxy;
+    }
+    public ProxyProperties getHttpProxy() {
+        return mHttpProxy;
+    }
+
+    public void clear() {
+        mIfaceName = null;
+        mLinkAddresses.clear();
+        mDnses.clear();
+        mRoutes.clear();
+        mHttpProxy = null;
+    }
+
+    /**
+     * Implement the Parcelable interface
+     * @hide
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " ");
+
+        String linkAddresses = "LinkAddresses: [";
+        for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString() + ",";
+        linkAddresses += "] ";
+
+        String dns = "DnsAddresses: [";
+        for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
+        dns += "] ";
+
+        String routes = "Routes: [";
+        for (RouteInfo route : mRoutes) routes += route.toString() + ",";
+        routes += "] ";
+        String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " ");
+
+        return ifaceName + linkAddresses + routes + dns + proxy;
+    }
+
+    /**
+     * Compares this {@code LinkProperties} interface name against the target
+     *
+     * @param target LinkProperties to compare.
+     * @return {@code true} if both are identical, {@code false} otherwise.
+     */
+    public boolean isIdenticalInterfaceName(LinkProperties target) {
+        return TextUtils.equals(getInterfaceName(), target.getInterfaceName());
+    }
+
+    /**
+     * Compares this {@code LinkProperties} interface name against the target
+     *
+     * @param target LinkProperties to compare.
+     * @return {@code true} if both are identical, {@code false} otherwise.
+     */
+    public boolean isIdenticalAddresses(LinkProperties target) {
+        Collection<InetAddress> targetAddresses = target.getAddresses();
+        Collection<InetAddress> sourceAddresses = getAddresses();
+        return (sourceAddresses.size() == targetAddresses.size()) ?
+                    sourceAddresses.containsAll(targetAddresses) : false;
+    }
+
+    /**
+     * Compares this {@code LinkProperties} DNS addresses against the target
+     *
+     * @param target LinkProperties to compare.
+     * @return {@code true} if both are identical, {@code false} otherwise.
+     */
+    public boolean isIdenticalDnses(LinkProperties target) {
+        Collection<InetAddress> targetDnses = target.getDnses();
+        return (mDnses.size() == targetDnses.size()) ?
+                    mDnses.containsAll(targetDnses) : false;
+    }
+
+    /**
+     * Compares this {@code LinkProperties} Routes against the target
+     *
+     * @param target LinkProperties to compare.
+     * @return {@code true} if both are identical, {@code false} otherwise.
+     */
+    public boolean isIdenticalRoutes(LinkProperties target) {
+        Collection<RouteInfo> targetRoutes = target.getRoutes();
+        return (mRoutes.size() == targetRoutes.size()) ?
+                    mRoutes.containsAll(targetRoutes) : false;
+    }
+
+    /**
+     * Compares this {@code LinkProperties} HttpProxy against the target
+     *
+     * @param target LinkProperties to compare.
+     * @return {@code true} if both are identical, {@code false} otherwise.
+     */
+    public boolean isIdenticalHttpProxy(LinkProperties target) {
+        return getHttpProxy() == null ? target.getHttpProxy() == null :
+                    getHttpProxy().equals(target.getHttpProxy());
+    }
+
+    @Override
+    /**
+     * Compares this {@code LinkProperties} instance against the target
+     * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
+     * all their fields are equal in values.
+     *
+     * For collection fields, such as mDnses, containsAll() is used to check
+     * if two collections contains the same elements, independent of order.
+     * There are two thoughts regarding containsAll()
+     * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal.
+     * 2. Worst case performance is O(n^2).
+     *
+     * @param obj the object to be tested for equality.
+     * @return {@code true} if both objects are equal, {@code false} otherwise.
+     */
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+
+        if (!(obj instanceof LinkProperties)) return false;
+
+        LinkProperties target = (LinkProperties) obj;
+
+        return isIdenticalInterfaceName(target) &&
+                isIdenticalAddresses(target) &&
+                isIdenticalDnses(target) &&
+                isIdenticalRoutes(target) &&
+                isIdenticalHttpProxy(target);
+    }
+
+    /**
+     * Return two lists, a list of addresses that would be removed from
+     * mLinkAddresses and a list of addresses that would be added to
+     * mLinkAddress which would then result in target and mLinkAddresses
+     * being the same list.
+     *
+     * @param target is a LinkProperties with the new list of addresses
+     * @return the removed and added lists.
+     */
+    public CompareResult<LinkAddress> compareAddresses(LinkProperties target) {
+        /*
+         * Duplicate the LinkAddresses into removed, we will be removing
+         * address which are common between mLinkAddresses and target
+         * leaving the addresses that are different. And address which
+         * are in target but not in mLinkAddresses are placed in the
+         * addedAddresses.
+         */
+        CompareResult<LinkAddress> result = new CompareResult<LinkAddress>();
+        result.removed = new ArrayList<LinkAddress>(mLinkAddresses);
+        result.added.clear();
+        if (target != null) {
+            for (LinkAddress newAddress : target.getLinkAddresses()) {
+                if (! result.removed.remove(newAddress)) {
+                    result.added.add(newAddress);
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Return two lists, a list of dns addresses that would be removed from
+     * mDnses and a list of addresses that would be added to
+     * mDnses which would then result in target and mDnses
+     * being the same list.
+     *
+     * @param target is a LinkProperties with the new list of dns addresses
+     * @return the removed and added lists.
+     */
+    public CompareResult<InetAddress> compareDnses(LinkProperties target) {
+        /*
+         * Duplicate the InetAddresses into removed, we will be removing
+         * dns address which are common between mDnses and target
+         * leaving the addresses that are different. And dns address which
+         * are in target but not in mDnses are placed in the
+         * addedAddresses.
+         */
+        CompareResult<InetAddress> result = new CompareResult<InetAddress>();
+
+        result.removed = new ArrayList<InetAddress>(mDnses);
+        result.added.clear();
+        if (target != null) {
+            for (InetAddress newAddress : target.getDnses()) {
+                if (! result.removed.remove(newAddress)) {
+                    result.added.add(newAddress);
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Return two lists, a list of routes that would be removed from
+     * mRoutes and a list of routes that would be added to
+     * mRoutes which would then result in target and mRoutes
+     * being the same list.
+     *
+     * @param target is a LinkProperties with the new list of routes
+     * @return the removed and added lists.
+     */
+    public CompareResult<RouteInfo> compareRoutes(LinkProperties target) {
+        /*
+         * Duplicate the RouteInfos into removed, we will be removing
+         * routes which are common between mDnses and target
+         * leaving the routes that are different. And route address which
+         * are in target but not in mRoutes are placed in added.
+         */
+        CompareResult<RouteInfo> result = new CompareResult<RouteInfo>();
+
+        result.removed = new ArrayList<RouteInfo>(mRoutes);
+        result.added.clear();
+        if (target != null) {
+            for (RouteInfo r : target.getRoutes()) {
+                if (! result.removed.remove(r)) {
+                    result.added.add(r);
+                }
+            }
+        }
+        return result;
+    }
+
+
+    @Override
+    /**
+     * generate hashcode based on significant fields
+     * Equal objects must produce the same hash code, while unequal objects
+     * may have the same hash codes.
+     */
+    public int hashCode() {
+        return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
+                + mLinkAddresses.size() * 31
+                + mDnses.size() * 37
+                + mRoutes.size() * 41
+                + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode()));
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(getInterfaceName());
+        dest.writeInt(mLinkAddresses.size());
+        for(LinkAddress linkAddress : mLinkAddresses) {
+            dest.writeParcelable(linkAddress, flags);
+        }
+
+        dest.writeInt(mDnses.size());
+        for(InetAddress d : mDnses) {
+            dest.writeByteArray(d.getAddress());
+        }
+
+        dest.writeInt(mRoutes.size());
+        for(RouteInfo route : mRoutes) {
+            dest.writeParcelable(route, flags);
+        }
+
+        if (mHttpProxy != null) {
+            dest.writeByte((byte)1);
+            dest.writeParcelable(mHttpProxy, flags);
+        } else {
+            dest.writeByte((byte)0);
+        }
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public static final Creator<LinkProperties> CREATOR =
+        new Creator<LinkProperties>() {
+            public LinkProperties createFromParcel(Parcel in) {
+                LinkProperties netProp = new LinkProperties();
+                String iface = in.readString();
+                if (iface != null) {
+                    try {
+                        netProp.setInterfaceName(iface);
+                    } catch (Exception e) {
+                        return null;
+                    }
+                }
+                int addressCount = in.readInt();
+                for (int i=0; i<addressCount; i++) {
+                    netProp.addLinkAddress((LinkAddress)in.readParcelable(null));
+                }
+                addressCount = in.readInt();
+                for (int i=0; i<addressCount; i++) {
+                    try {
+                        netProp.addDns(InetAddress.getByAddress(in.createByteArray()));
+                    } catch (UnknownHostException e) { }
+                }
+                addressCount = in.readInt();
+                for (int i=0; i<addressCount; i++) {
+                    netProp.addRoute((RouteInfo)in.readParcelable(null));
+                }
+                if (in.readByte() == 1) {
+                    netProp.setHttpProxy((ProxyProperties)in.readParcelable(null));
+                }
+                return netProp;
+            }
+
+            public LinkProperties[] newArray(int size) {
+                return new LinkProperties[size];
+            }
+        };
+}
diff --git a/core/java/android/net/NetworkConfig.java b/core/java/android/net/NetworkConfig.java
new file mode 100644
index 0000000..3cc0bc5
--- /dev/null
+++ b/core/java/android/net/NetworkConfig.java
@@ -0,0 +1,80 @@
+/*
+ * 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;
+
+import android.util.Log;
+
+/**
+ * Describes the buildtime configuration of a network.
+ * Holds settings read from resources.
+ * @hide
+ */
+public class NetworkConfig {
+    /**
+     * Human readable string
+     */
+    public String name;
+
+    /**
+     * Type from ConnectivityManager
+     */
+    public int type;
+
+    /**
+     * the radio number from radio attributes config
+     */
+    public int radio;
+
+    /**
+     * higher number == higher priority when turning off connections
+     */
+    public int priority;
+
+    /**
+     * indicates the boot time dependencyMet setting
+     */
+    public boolean dependencyMet;
+
+    /**
+     * indicates the default restoral timer in seconds
+     * if the network is used as a special network feature
+     * -1 indicates no restoration of default
+     */
+    public int restoreTime;
+
+    /**
+     * input string from config.xml resource.  Uses the form:
+     * [Connection name],[ConnectivityManager connection type],
+     * [associated radio-type],[priority],[dependencyMet]
+     */
+    public NetworkConfig(String init) {
+        String fragments[] = init.split(",");
+        name = fragments[0].trim().toLowerCase();
+        type = Integer.parseInt(fragments[1]);
+        radio = Integer.parseInt(fragments[2]);
+        priority = Integer.parseInt(fragments[3]);
+        restoreTime = Integer.parseInt(fragments[4]);
+        dependencyMet = Boolean.parseBoolean(fragments[5]);
+    }
+
+    /**
+     * Indicates if this network is supposed to be default-routable
+     */
+    public boolean isDefault() {
+        return (type == radio);
+    }
+}
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 649cb8c..5f5e11c 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -97,7 +97,7 @@
         stateMap.put(DetailedState.DISCONNECTED, State.DISCONNECTED);
         stateMap.put(DetailedState.FAILED, State.DISCONNECTED);
     }
-    
+
     private int mNetworkType;
     private int mSubtype;
     private String mTypeName;
@@ -121,7 +121,10 @@
      */
     public NetworkInfo(int type) {}
 
-    NetworkInfo(int type, int subtype, String typeName, String subtypeName) {
+    /**
+     * @hide
+     */
+    public NetworkInfo(int type, int subtype, String typeName, String subtypeName) {
         if (!ConnectivityManager.isNetworkTypeValid(type)) {
             throw new IllegalArgumentException("Invalid network type: " + type);
         }
@@ -141,7 +144,9 @@
      * @return the network type
      */
     public int getType() {
-        return mNetworkType;
+        synchronized (this) {
+            return mNetworkType;
+        }
     }
 
     /**
@@ -150,12 +155,16 @@
      * @return the network subtype
      */
     public int getSubtype() {
-        return mSubtype;
+        synchronized (this) {
+            return mSubtype;
+        }
     }
 
     void setSubtype(int subtype, String subtypeName) {
-        mSubtype = subtype;
-        mSubtypeName = subtypeName;
+        synchronized (this) {
+            mSubtype = subtype;
+            mSubtypeName = subtypeName;
+        }
     }
 
     /**
@@ -164,7 +173,9 @@
      * @return the name of the network type
      */
     public String getTypeName() {
-        return mTypeName;
+        synchronized (this) {
+            return mTypeName;
+        }
     }
 
     /**
@@ -172,7 +183,9 @@
      * @return the name of the network subtype
      */
     public String getSubtypeName() {
-        return mSubtypeName;
+        synchronized (this) {
+            return mSubtypeName;
+        }
     }
 
     /**
@@ -185,7 +198,9 @@
      * of being established, {@code false} otherwise.
      */
     public boolean isConnectedOrConnecting() {
-        return mState == State.CONNECTED || mState == State.CONNECTING;
+        synchronized (this) {
+            return mState == State.CONNECTED || mState == State.CONNECTING;
+        }
     }
 
     /**
@@ -194,7 +209,9 @@
      * @return {@code true} if network connectivity exists, {@code false} otherwise.
      */
     public boolean isConnected() {
-        return mState == State.CONNECTED;
+        synchronized (this) {
+            return mState == State.CONNECTED;
+        }
     }
 
     /**
@@ -210,7 +227,9 @@
      * @return {@code true} if the network is available, {@code false} otherwise
      */
     public boolean isAvailable() {
-        return mIsAvailable;
+        synchronized (this) {
+            return mIsAvailable;
+        }
     }
 
     /**
@@ -220,7 +239,9 @@
      * @hide
      */
     public void setIsAvailable(boolean isAvailable) {
-        mIsAvailable = isAvailable;
+        synchronized (this) {
+            mIsAvailable = isAvailable;
+        }
     }
 
     /**
@@ -231,7 +252,9 @@
      * otherwise.
      */
     public boolean isFailover() {
-        return mIsFailover;
+        synchronized (this) {
+            return mIsFailover;
+        }
     }
 
     /**
@@ -241,7 +264,9 @@
      * @hide
      */
     public void setFailover(boolean isFailover) {
-        mIsFailover = isFailover;
+        synchronized (this) {
+            mIsFailover = isFailover;
+        }
     }
 
     /**
@@ -251,11 +276,15 @@
      * @return {@code true} if roaming is in effect, {@code false} otherwise.
      */
     public boolean isRoaming() {
-        return mIsRoaming;
+        synchronized (this) {
+            return mIsRoaming;
+        }
     }
 
     void setRoaming(boolean isRoaming) {
-        mIsRoaming = isRoaming;
+        synchronized (this) {
+            mIsRoaming = isRoaming;
+        }
     }
 
     /**
@@ -263,7 +292,9 @@
      * @return the coarse-grained state
      */
     public State getState() {
-        return mState;
+        synchronized (this) {
+            return mState;
+        }
     }
 
     /**
@@ -271,7 +302,9 @@
      * @return the fine-grained state
      */
     public DetailedState getDetailedState() {
-        return mDetailedState;
+        synchronized (this) {
+            return mDetailedState;
+        }
     }
 
     /**
@@ -281,12 +314,15 @@
      * if one was supplied. May be {@code null}.
      * @param extraInfo an optional {@code String} providing addditional network state
      * information passed up from the lower networking layers.
+     * @hide
      */
-    void setDetailedState(DetailedState detailedState, String reason, String extraInfo) {
-        this.mDetailedState = detailedState;
-        this.mState = stateMap.get(detailedState);
-        this.mReason = reason;
-        this.mExtraInfo = extraInfo;
+    public void setDetailedState(DetailedState detailedState, String reason, String extraInfo) {
+        synchronized (this) {
+            this.mDetailedState = detailedState;
+            this.mState = stateMap.get(detailedState);
+            this.mReason = reason;
+            this.mExtraInfo = extraInfo;
+        }
     }
 
     /**
@@ -295,7 +331,9 @@
      * @return the reason for failure, or null if not available
      */
     public String getReason() {
-        return mReason;
+        synchronized (this) {
+            return mReason;
+        }
     }
 
     /**
@@ -305,20 +343,24 @@
      * @return the extra information, or null if not available
      */
     public String getExtraInfo() {
-        return mExtraInfo;
+        synchronized (this) {
+            return mExtraInfo;
+        }
     }
 
     @Override
     public String toString() {
-        StringBuilder builder = new StringBuilder("NetworkInfo: ");
-        builder.append("type: ").append(getTypeName()).append("[").append(getSubtypeName()).
-                append("], state: ").append(mState).append("/").append(mDetailedState).
-                append(", reason: ").append(mReason == null ? "(unspecified)" : mReason).
-                append(", extra: ").append(mExtraInfo == null ? "(none)" : mExtraInfo).
-                append(", roaming: ").append(mIsRoaming).
-                append(", failover: ").append(mIsFailover).
-                append(", isAvailable: ").append(mIsAvailable);
-        return builder.toString();
+        synchronized (this) {
+            StringBuilder builder = new StringBuilder("NetworkInfo: ");
+            builder.append("type: ").append(getTypeName()).append("[").append(getSubtypeName()).
+            append("], state: ").append(mState).append("/").append(mDetailedState).
+            append(", reason: ").append(mReason == null ? "(unspecified)" : mReason).
+            append(", extra: ").append(mExtraInfo == null ? "(none)" : mExtraInfo).
+            append(", roaming: ").append(mIsRoaming).
+            append(", failover: ").append(mIsFailover).
+            append(", isAvailable: ").append(mIsAvailable);
+            return builder.toString();
+        }
     }
 
     /**
@@ -334,17 +376,19 @@
      * @hide
      */
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mNetworkType);
-        dest.writeInt(mSubtype);
-        dest.writeString(mTypeName);
-        dest.writeString(mSubtypeName);
-        dest.writeString(mState.name());
-        dest.writeString(mDetailedState.name());
-        dest.writeInt(mIsFailover ? 1 : 0);
-        dest.writeInt(mIsAvailable ? 1 : 0);
-        dest.writeInt(mIsRoaming ? 1 : 0);
-        dest.writeString(mReason);
-        dest.writeString(mExtraInfo);
+        synchronized (this) {
+            dest.writeInt(mNetworkType);
+            dest.writeInt(mSubtype);
+            dest.writeString(mTypeName);
+            dest.writeString(mSubtypeName);
+            dest.writeString(mState.name());
+            dest.writeString(mDetailedState.name());
+            dest.writeInt(mIsFailover ? 1 : 0);
+            dest.writeInt(mIsAvailable ? 1 : 0);
+            dest.writeInt(mIsRoaming ? 1 : 0);
+            dest.writeString(mReason);
+            dest.writeString(mExtraInfo);
+        }
     }
 
     /**
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 8bdfdf9..76534ef 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -38,30 +38,22 @@
     /** Bring the named network interface down. */
     public native static int disableInterface(String interfaceName);
 
+    /** Setting bit 0 indicates reseting of IPv4 addresses required */
+    public static final int RESET_IPV4_ADDRESSES = 0x01;
+
+    /** Setting bit 1 indicates reseting of IPv4 addresses required */
+    public static final int RESET_IPV6_ADDRESSES = 0x02;
+
+    /** Reset all addresses */
+    public static final int RESET_ALL_ADDRESSES = RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES;
+
     /**
-     * Add a route to the routing table.
+     * Reset IPv6 or IPv4 sockets that are connected via the named interface.
      *
-     * @param interfaceName the interface to route through.
-     * @param dst the network or host to route to. May be IPv4 or IPv6, e.g.
-     * "0.0.0.0" or "2001:4860::".
-     * @param prefixLength the prefix length of the route.
-     * @param gw the gateway to use, e.g., "192.168.251.1". If null,
-     * indicates a directly-connected route.
+     * @param interfaceName is the interface to reset
+     * @param mask {@see #RESET_IPV4_ADDRESSES} and {@see #RESET_IPV6_ADDRESSES}
      */
-    public native static int addRoute(String interfaceName, String dst,
-          int prefixLength, String gw);
-
-    /** Return the gateway address for the default route for the named interface. */
-    public native static int getDefaultRoute(String interfaceName);
-
-    /** Remove host routes that uses the named interface. */
-    public native static int removeHostRoutes(String interfaceName);
-
-    /** Remove the default route for the named interface. */
-    public native static int removeDefaultRoute(String interfaceName);
-
-    /** Reset any sockets that are connected via the named interface. */
-    public native static int resetConnections(String interfaceName);
+    public native static int resetConnections(String interfaceName, int mask);
 
     /**
      * Start the DHCP client daemon, in order to have it request addresses
@@ -73,7 +65,17 @@
      * the IP address information.
      * @return {@code true} for success, {@code false} for failure
      */
-    public native static boolean runDhcp(String interfaceName, DhcpInfo ipInfo);
+    public native static boolean runDhcp(String interfaceName, DhcpInfoInternal ipInfo);
+
+    /**
+     * Initiate renewal on the Dhcp client daemon. This call blocks until it obtains
+     * a result (either success or failure) from the daemon.
+     * @param interfaceName the name of the interface to configure
+     * @param ipInfo if the request succeeds, this object is filled in with
+     * the IP address information.
+     * @return {@code true} for success, {@code false} for failure
+     */
+    public native static boolean runDhcpRenew(String interfaceName, DhcpInfoInternal ipInfo);
 
     /**
      * Shut down the DHCP client daemon.
@@ -100,125 +102,137 @@
     public native static String getDhcpError();
 
     /**
-     * When static IP configuration has been specified, configure the network
-     * interface according to the values supplied.
-     * @param interfaceName the name of the interface to configure
-     * @param ipInfo the IP address, default gateway, and DNS server addresses
-     * with which to configure the interface.
-     * @return {@code true} for success, {@code false} for failure
-     */
-    public static boolean configureInterface(String interfaceName, DhcpInfo ipInfo) {
-        return configureNative(interfaceName,
-            ipInfo.ipAddress,
-            ipInfo.netmask,
-            ipInfo.gateway,
-            ipInfo.dns1,
-            ipInfo.dns2);
-    }
-
-    private native static boolean configureNative(
-        String interfaceName, int ipAddress, int netmask, int gateway, int dns1, int dns2);
-
-    /**
      * Convert a IPv4 address from an integer to an InetAddress.
-     * @param hostAddr is an Int corresponding to the IPv4 address in network byte order
-     * @return the IP address as an {@code InetAddress}, returns null if
-     * unable to convert or if the int is an invalid address.
+     * @param hostAddress an int corresponding to the IPv4 address in network byte order
      */
     public static InetAddress intToInetAddress(int hostAddress) {
-        InetAddress inetAddress;
         byte[] addressBytes = { (byte)(0xff & hostAddress),
                                 (byte)(0xff & (hostAddress >> 8)),
                                 (byte)(0xff & (hostAddress >> 16)),
                                 (byte)(0xff & (hostAddress >> 24)) };
 
         try {
-           inetAddress = InetAddress.getByAddress(addressBytes);
-        } catch(UnknownHostException e) {
-           return null;
+           return InetAddress.getByAddress(addressBytes);
+        } catch (UnknownHostException e) {
+           throw new AssertionError();
         }
-
-        return inetAddress;
     }
 
     /**
-     * Add a default route through the specified gateway.
-     * @param interfaceName interface on which the route should be added
-     * @param gw the IP address of the gateway to which the route is desired,
-     * @return {@code true} on success, {@code false} on failure
+     * Convert a IPv4 address from an InetAddress to an integer
+     * @param inetAddr is an InetAddress corresponding to the IPv4 address
+     * @return the IP address as an integer in network byte order
      */
-    public static boolean addDefaultRoute(String interfaceName, InetAddress gw) {
-        String dstStr;
-        String gwStr = gw.getHostAddress();
-
-        if (gw instanceof Inet4Address) {
-            dstStr = "0.0.0.0";
-        } else if (gw instanceof Inet6Address) {
-            dstStr = "::";
-        } else {
-            Log.w(TAG, "addDefaultRoute failure: address is neither IPv4 nor IPv6" +
-                       "(" + gwStr + ")");
-            return false;
+    public static int inetAddressToInt(InetAddress inetAddr)
+            throws IllegalArgumentException {
+        byte [] addr = inetAddr.getAddress();
+        if (addr.length != 4) {
+            throw new IllegalArgumentException("Not an IPv4 address");
         }
-        return addRoute(interfaceName, dstStr, 0, gwStr) == 0;
+        return ((addr[3] & 0xff) << 24) | ((addr[2] & 0xff) << 16) |
+                ((addr[1] & 0xff) << 8) | (addr[0] & 0xff);
     }
 
     /**
-     * Add a host route.
-     * @param interfaceName interface on which the route should be added
-     * @param dst the IP address of the host to which the route is desired,
-     * this should not be null.
-     * @param gw the IP address of the gateway to which the route is desired,
-     * if null, indicates a directly-connected route.
-     * @return {@code true} on success, {@code false} on failure
+     * Convert a network prefix length to an IPv4 netmask integer
+     * @param prefixLength
+     * @return the IPv4 netmask as an integer in network byte order
      */
-    public static boolean addHostRoute(String interfaceName, InetAddress dst,
-          InetAddress gw) {
-        if (dst == null) {
-            Log.w(TAG, "addHostRoute: dst should not be null");
-            return false;
+    public static int prefixLengthToNetmaskInt(int prefixLength)
+            throws IllegalArgumentException {
+        if (prefixLength < 0 || prefixLength > 32) {
+            throw new IllegalArgumentException("Invalid prefix length (0 <= prefix <= 32)");
         }
-
-        int prefixLength;
-        String dstStr = dst.getHostAddress();
-        String gwStr = (gw != null) ? gw.getHostAddress() : null;
-
-        if (dst instanceof Inet4Address) {
-            prefixLength = 32;
-        } else if (dst instanceof Inet6Address) {
-            prefixLength = 128;
-        } else {
-            Log.w(TAG, "addHostRoute failure: address is neither IPv4 nor IPv6" +
-                       "(" + dst + ")");
-            return false;
-        }
-        return addRoute(interfaceName, dstStr, prefixLength, gwStr) == 0;
+        int value = 0xffffffff << (32 - prefixLength);
+        return Integer.reverseBytes(value);
     }
 
-    public static int v4StringToInt(String str) {
-        int result = 0;
-        String[] array = str.split("\\.");
-        if (array.length != 4) return 0;
+    /**
+     * Convert a IPv4 netmask integer to a prefix length
+     * @param netmask as an integer in network byte order
+     * @return the network prefix length
+     */
+    public static int netmaskIntToPrefixLength(int netmask) {
+        return Integer.bitCount(netmask);
+    }
+
+    /**
+     * Create an InetAddress from a string where the string must be a standard
+     * representation of a V4 or V6 address.  Avoids doing a DNS lookup on failure
+     * but it will throw an IllegalArgumentException in that case.
+     * @param addrString
+     * @return the InetAddress
+     * @hide
+     */
+    public static InetAddress numericToInetAddress(String addrString)
+            throws IllegalArgumentException {
+        return InetAddress.parseNumericAddress(addrString);
+    }
+
+    /**
+     * Get InetAddress masked with prefixLength.  Will never return null.
+     * @param IP address which will be masked with specified prefixLength
+     * @param prefixLength the prefixLength used to mask the IP
+     */
+    public static InetAddress getNetworkPart(InetAddress address, int prefixLength) {
+        if (address == null) {
+            throw new RuntimeException("getNetworkPart doesn't accept null address");
+        }
+
+        byte[] array = address.getAddress();
+
+        if (prefixLength < 0 || prefixLength > array.length * 8) {
+            throw new RuntimeException("getNetworkPart - bad prefixLength");
+        }
+
+        int offset = prefixLength / 8;
+        int reminder = prefixLength % 8;
+        byte mask = (byte)(0xFF << (8 - reminder));
+
+        if (offset < array.length) array[offset] = (byte)(array[offset] & mask);
+
+        offset++;
+
+        for (; offset < array.length; offset++) {
+            array[offset] = 0;
+        }
+
+        InetAddress netPart = null;
         try {
-            result = Integer.parseInt(array[3]);
-            result = (result << 8) + Integer.parseInt(array[2]);
-            result = (result << 8) + Integer.parseInt(array[1]);
-            result = (result << 8) + Integer.parseInt(array[0]);
-        } catch (NumberFormatException e) {
-            return 0;
+            netPart = InetAddress.getByAddress(array);
+        } catch (UnknownHostException e) {
+            throw new RuntimeException("getNetworkPart error - " + e.toString());
         }
-        return result;
+        return netPart;
     }
 
     /**
-     * Start the DHCP renew service for wimax,
-     * This call blocks until it obtains a result (either success
-     * or failure) from the daemon.
-     * @param interfaceName the name of the interface to configure
-     * @param ipInfo if the request succeeds, this object is filled in with
-     * the IP address information.
-     * @return {@code true} for success, {@code false} for failure
-     * {@hide}
+     * Check if IP address type is consistent between two InetAddress.
+     * @return true if both are the same type.  False otherwise.
      */
-    public native static boolean runDhcpRenew(String interfaceName, DhcpInfo ipInfo);
+    public static boolean addressTypeMatches(InetAddress left, InetAddress right) {
+        return (((left instanceof Inet4Address) && (right instanceof Inet4Address)) ||
+                ((left instanceof Inet6Address) && (right instanceof Inet6Address)));
+    }
+
+    /**
+     * Convert a 32 char hex string into a Inet6Address.
+     * throws a runtime exception if the string isn't 32 chars, isn't hex or can't be
+     * made into an Inet6Address
+     * @param addrHexString a 32 character hex string representing an IPv6 addr
+     * @return addr an InetAddress representation for the string
+     */
+    public static InetAddress hexToInet6Address(String addrHexString)
+            throws IllegalArgumentException {
+        try {
+            return numericToInetAddress(String.format("%s:%s:%s:%s:%s:%s:%s:%s",
+                    addrHexString.substring(0,4),   addrHexString.substring(4,8),
+                    addrHexString.substring(8,12),  addrHexString.substring(12,16),
+                    addrHexString.substring(16,20), addrHexString.substring(20,24),
+                    addrHexString.substring(24,28), addrHexString.substring(28,32)));
+        } catch (Exception e) {
+            Log.e("NetworkUtils", "error in hexToInet6Address(" + addrHexString + "): " + e);
+            throw new IllegalArgumentException(e);
+        }
+    }
 }
diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyProperties.java
new file mode 100644
index 0000000..44dbec1
--- /dev/null
+++ b/core/java/android/net/ProxyProperties.java
@@ -0,0 +1,216 @@
+/*
+ * 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;
+
+
+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;
+
+/**
+ * A container class for the http proxy info
+ * @hide
+ */
+public class ProxyProperties implements Parcelable {
+
+    private String mHost;
+    private int mPort;
+    private String mExclusionList;
+    private String[] mParsedExclusionList;
+
+    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) {
+            mHost = source.getHost();
+            mPort = source.getPort();
+            mExclusionList = source.getExclusionList();
+            mParsedExclusionList = source.mParsedExclusionList;
+        }
+    }
+
+    public InetSocketAddress getSocketAddress() {
+        InetSocketAddress inetSocketAddress = null;
+        try {
+            inetSocketAddress = new InetSocketAddress(mHost, mPort);
+        } catch (IllegalArgumentException e) { }
+        return inetSocketAddress;
+    }
+
+    public String getHost() {
+        return mHost;
+    }
+
+    public int getPort() {
+        return mPort;
+    }
+
+    // comma separated
+    public String getExclusionList() {
+        return mExclusionList;
+    }
+
+    // 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 (mHost != null) {
+            sb.append("[");
+            sb.append(mHost);
+            sb.append("] ");
+            sb.append(Integer.toString(mPort));
+            if (mExclusionList != null) {
+                    sb.append(" xl=").append(mExclusionList);
+            }
+        }
+        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
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    /*
+     * generate hashcode based on significant fields
+     */
+    public int hashCode() {
+        return ((null == mHost) ? 0 : mHost.hashCode())
+        + ((null == mExclusionList) ? 0 : mExclusionList.hashCode())
+        + mPort;
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mHost != null) {
+            dest.writeByte((byte)1);
+            dest.writeString(mHost);
+            dest.writeInt(mPort);
+        } else {
+            dest.writeByte((byte)0);
+        }
+        dest.writeString(mExclusionList);
+        dest.writeStringArray(mParsedExclusionList);
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public static final Creator<ProxyProperties> CREATOR =
+        new Creator<ProxyProperties>() {
+            public ProxyProperties createFromParcel(Parcel in) {
+                String host = null;
+                int port = 0;
+                if (in.readByte() == 1) {
+                    host = in.readString();
+                    port = in.readInt();
+                }
+                String exclList = in.readString();
+                String[] parsedExclList = in.readStringArray();
+                ProxyProperties proxyProperties =
+                        new ProxyProperties(host, port, exclList, parsedExclList);
+                return proxyProperties;
+            }
+
+            public ProxyProperties[] newArray(int size) {
+                return new ProxyProperties[size];
+            }
+        };
+}
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
new file mode 100644
index 0000000..275f32a
--- /dev/null
+++ b/core/java/android/net/RouteInfo.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2011 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.UnknownHostException;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+
+import java.util.Collection;
+
+/**
+ * A simple container for route information.
+ *
+ * @hide
+ */
+public class RouteInfo implements Parcelable {
+    /**
+     * The IP destination address for this route.
+     */
+    private final LinkAddress mDestination;
+
+    /**
+     * The gateway address for this route.
+     */
+    private final InetAddress mGateway;
+
+    private final boolean mIsDefault;
+    private final boolean mIsHost;
+
+    public RouteInfo(LinkAddress destination, InetAddress gateway) {
+        if (destination == null) {
+            if (gateway != null) {
+                if (gateway instanceof Inet4Address) {
+                    destination = new LinkAddress(Inet4Address.ANY, 0);
+                } else {
+                    destination = new LinkAddress(Inet6Address.ANY, 0);
+                }
+            } else {
+                // no destination, no gateway. invalid.
+                throw new RuntimeException("Invalid arguments passed in.");
+            }
+        }
+        if (gateway == null) {
+            if (destination.getAddress() instanceof Inet4Address) {
+                gateway = Inet4Address.ANY;
+            } else {
+                gateway = Inet6Address.ANY;
+            }
+        }
+        mDestination = new LinkAddress(NetworkUtils.getNetworkPart(destination.getAddress(),
+                destination.getNetworkPrefixLength()), destination.getNetworkPrefixLength());
+        mGateway = gateway;
+        mIsDefault = isDefault();
+        mIsHost = isHost();
+    }
+
+    public RouteInfo(InetAddress gateway) {
+        this(null, gateway);
+    }
+
+    public static RouteInfo makeHostRoute(InetAddress host) {
+        return makeHostRoute(host, null);
+    }
+
+    public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway) {
+        if (host == null) return null;
+
+        if (host instanceof Inet4Address) {
+            return new RouteInfo(new LinkAddress(host, 32), gateway);
+        } else {
+            return new RouteInfo(new LinkAddress(host, 128), gateway);
+        }
+    }
+
+    private boolean isHost() {
+        return (mGateway.equals(Inet4Address.ANY) || mGateway.equals(Inet6Address.ANY));
+    }
+
+    private boolean isDefault() {
+        boolean val = false;
+        if (mGateway != null) {
+            if (mGateway instanceof Inet4Address) {
+                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
+            } else {
+                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
+            }
+        }
+        return val;
+    }
+
+
+    public LinkAddress getDestination() {
+        return mDestination;
+    }
+
+    public InetAddress getGateway() {
+        return mGateway;
+    }
+
+    public boolean isDefaultRoute() {
+        return mIsDefault;
+    }
+
+    public boolean isHostRoute() {
+        return mIsHost;
+    }
+
+    public String toString() {
+        String val = "";
+        if (mDestination != null) val = mDestination.toString();
+        if (mGateway != null) val += " -> " + mGateway.getHostAddress();
+        return val;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mDestination == null) {
+            dest.writeByte((byte) 0);
+        } else {
+            dest.writeByte((byte) 1);
+            dest.writeByteArray(mDestination.getAddress().getAddress());
+            dest.writeInt(mDestination.getNetworkPrefixLength());
+        }
+
+        if (mGateway == null) {
+            dest.writeByte((byte) 0);
+        } else {
+            dest.writeByte((byte) 1);
+            dest.writeByteArray(mGateway.getAddress());
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+
+        if (!(obj instanceof RouteInfo)) return false;
+
+        RouteInfo target = (RouteInfo) obj;
+
+        boolean sameDestination = ( mDestination == null) ?
+                target.getDestination() == null
+                : mDestination.equals(target.getDestination());
+
+        boolean sameAddress = (mGateway == null) ?
+                target.getGateway() == null
+                : mGateway.equals(target.getGateway());
+
+        return sameDestination && sameAddress
+            && mIsDefault == target.mIsDefault;
+    }
+
+    @Override
+    public int hashCode() {
+        return (mDestination == null ? 0 : mDestination.hashCode())
+            + (mGateway == null ? 0 :mGateway.hashCode())
+            + (mIsDefault ? 3 : 7);
+    }
+
+    public static final Creator<RouteInfo> CREATOR =
+        new Creator<RouteInfo>() {
+        public RouteInfo createFromParcel(Parcel in) {
+            InetAddress destAddr = null;
+            int prefix = 0;
+            InetAddress gateway = null;
+
+            if (in.readByte() == 1) {
+                byte[] addr = in.createByteArray();
+                prefix = in.readInt();
+
+                try {
+                    destAddr = InetAddress.getByAddress(addr);
+                } catch (UnknownHostException e) {}
+            }
+
+            if (in.readByte() == 1) {
+                byte[] addr = in.createByteArray();
+
+                try {
+                    gateway = InetAddress.getByAddress(addr);
+                } catch (UnknownHostException e) {}
+            }
+
+            LinkAddress dest = null;
+
+            if (destAddr != null) {
+                dest = new LinkAddress(destAddr, prefix);
+            }
+
+            return new RouteInfo(dest, gateway);
+        }
+
+        public RouteInfo[] newArray(int size) {
+            return new RouteInfo[size];
+        }
+    };
+
+    private boolean matches(InetAddress destination) {
+        if (destination == null) return false;
+
+        // if the destination is present and the route is default.
+        // return true
+        if (isDefault()) return true;
+
+        // match the route destination and destination with prefix length
+        InetAddress dstNet = NetworkUtils.getNetworkPart(destination,
+                mDestination.getNetworkPrefixLength());
+
+        return mDestination.getAddress().equals(dstNet);
+    }
+
+    /**
+     * Find the route from a Collection of routes that best matches a given address.
+     * May return null if no routes are applicable.
+     * @param routes a Collection of RouteInfos to chose from
+     * @param dest the InetAddress your trying to get to
+     * @return the RouteInfo from the Collection that best fits the given address
+     */
+    public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
+        if ((routes == null) || (dest == null)) return null;
+
+        RouteInfo bestRoute = null;
+        // pick a longest prefix match under same address type
+        for (RouteInfo route : routes) {
+            if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
+                if ((bestRoute != null) &&
+                        (bestRoute.mDestination.getNetworkPrefixLength() >=
+                        route.mDestination.getNetworkPrefixLength())) {
+                    continue;
+                }
+                if (route.matches(dest)) bestRoute = route;
+            }
+        }
+        return bestRoute;
+    }
+}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 4d9bb3b..068fe67 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -21,37 +21,34 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Log.h>
 #include <arpa/inet.h>
+#include <cutils/properties.h>
 
 extern "C" {
 int ifc_enable(const char *ifname);
 int ifc_disable(const char *ifname);
-int ifc_add_route(const char *ifname, const char *destStr, uint32_t prefixLen, const char *gwStr);
-int ifc_remove_host_routes(const char *ifname);
-int ifc_get_default_route(const char *ifname);
-int ifc_remove_default_route(const char *ifname);
-int ifc_reset_connections(const char *ifname);
-int ifc_configure(const char *ifname, in_addr_t ipaddr, in_addr_t netmask, in_addr_t gateway, in_addr_t dns1, in_addr_t dns2);
+int ifc_reset_connections(const char *ifname, int reset_mask);
 
 int dhcp_do_request(const char *ifname,
-                    in_addr_t *ipaddr,
-                    in_addr_t *gateway,
-                    in_addr_t *mask,
-                    in_addr_t *dns1,
-                    in_addr_t *dns2,
-                    in_addr_t *server,
+                    const char *ipaddr,
+                    const char *gateway,
+                    uint32_t  *prefixLength,
+                    const char *dns1,
+                    const char *dns2,
+                    const char *server,
                     uint32_t  *lease);
+
+int dhcp_do_request_renew(const char *ifname,
+                    const char *ipaddr,
+                    const char *gateway,
+                    uint32_t  *prefixLength,
+                    const char *dns1,
+                    const char *dns2,
+                    const char *server,
+                    uint32_t  *lease);
+
 int dhcp_stop(const char *ifname);
 int dhcp_release_lease(const char *ifname);
 char *dhcp_get_errmsg();
-
-int dhcp_do_request_renew(const char *ifname,
-                    in_addr_t *ipaddr,
-                    in_addr_t *gateway,
-                    in_addr_t *mask,
-                    in_addr_t *dns1,
-                    in_addr_t *dns2,
-                    in_addr_t *server,
-                    uint32_t  *lease);
 }
 
 #define NETUTILS_PKG_NAME "android/net/NetworkUtils"
@@ -64,16 +61,15 @@
  * to look them up every time.
  */
 static struct fieldIds {
-    jclass dhcpInfoClass;
+    jclass dhcpInfoInternalClass;
     jmethodID constructorId;
     jfieldID ipaddress;
-    jfieldID gateway;
-    jfieldID netmask;
+    jfieldID prefixLength;
     jfieldID dns1;
     jfieldID dns2;
     jfieldID serverAddress;
     jfieldID leaseDuration;
-} dhcpInfoFieldIds;
+} dhcpInfoInternalFieldIds;
 
 static jint android_net_utils_enableInterface(JNIEnv* env, jobject clazz, jstring ifname)
 {
@@ -95,88 +91,92 @@
     return (jint)result;
 }
 
-static jint android_net_utils_addRoute(JNIEnv* env, jobject clazz, jstring ifname,
-          jstring dst, jint prefixLength, jstring gw)
+static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz,
+      jstring ifname, jint mask)
 {
     int result;
 
     const char *nameStr = env->GetStringUTFChars(ifname, NULL);
-    const char *dstStr = env->GetStringUTFChars(dst, NULL);
-    const char *gwStr = NULL;
-    if (gw != NULL) {
-        gwStr = env->GetStringUTFChars(gw, NULL);
+
+    LOGD("android_net_utils_resetConnections in env=%p clazz=%p iface=%s mask=0x%x\n",
+          env, clazz, nameStr, mask);
+
+    result = ::ifc_reset_connections(nameStr, mask);
+    env->ReleaseStringUTFChars(ifname, nameStr);
+    return (jint)result;
+}
+
+static jboolean android_net_utils_runDhcpCommon(JNIEnv* env, jobject clazz, jstring ifname,
+        jobject info, bool renew)
+{
+    int result;
+    char  ipaddr[PROPERTY_VALUE_MAX];
+    uint32_t prefixLength;
+    char gateway[PROPERTY_VALUE_MAX];
+    char    dns1[PROPERTY_VALUE_MAX];
+    char    dns2[PROPERTY_VALUE_MAX];
+    char  server[PROPERTY_VALUE_MAX];
+    uint32_t lease;
+
+    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+    if (nameStr == NULL) return (jboolean)false;
+
+    if (renew) {
+        result = ::dhcp_do_request_renew(nameStr, ipaddr, gateway, &prefixLength,
+                dns1, dns2, server, &lease);
+    } else {
+        result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength,
+                dns1, dns2, server, &lease);
     }
-    result = ::ifc_add_route(nameStr, dstStr, prefixLength, gwStr);
+
     env->ReleaseStringUTFChars(ifname, nameStr);
-    env->ReleaseStringUTFChars(dst, dstStr);
-    if (gw != NULL) {
-        env->ReleaseStringUTFChars(gw, gwStr);
+    if (result == 0 && dhcpInfoInternalFieldIds.dhcpInfoInternalClass != NULL) {
+        env->SetObjectField(info, dhcpInfoInternalFieldIds.ipaddress, env->NewStringUTF(ipaddr));
+
+        // set the gateway
+        jclass cls = env->FindClass("java/net/InetAddress");
+        jmethodID method = env->GetStaticMethodID(cls, "getByName",
+                "(Ljava/lang/String;)Ljava/net/InetAddress;");
+        jvalue args[1];
+        args[0].l = env->NewStringUTF(gateway);
+        jobject inetAddressObject = env->CallStaticObjectMethodA(cls, method, args);
+
+        if (!env->ExceptionOccurred()) {
+            cls = env->FindClass("android/net/RouteInfo");
+            method = env->GetMethodID(cls, "<init>", "(Ljava/net/InetAddress;)V");
+            args[0].l = inetAddressObject;
+            jobject routeInfoObject = env->NewObjectA(cls, method, args);
+
+            cls = env->FindClass("android/net/DhcpInfoInternal");
+            method = env->GetMethodID(cls, "addRoute", "(Landroid/net/RouteInfo;)V");
+            args[0].l = routeInfoObject;
+            env->CallVoidMethodA(info, method, args);
+        } else {
+            // if we have an exception (host not found perhaps), just don't add the route
+            env->ExceptionClear();
+        }
+
+        env->SetIntField(info, dhcpInfoInternalFieldIds.prefixLength, prefixLength);
+        env->SetObjectField(info, dhcpInfoInternalFieldIds.dns1, env->NewStringUTF(dns1));
+        env->SetObjectField(info, dhcpInfoInternalFieldIds.dns2, env->NewStringUTF(dns2));
+        env->SetObjectField(info, dhcpInfoInternalFieldIds.serverAddress,
+                env->NewStringUTF(server));
+        env->SetIntField(info, dhcpInfoInternalFieldIds.leaseDuration, lease);
     }
-    return (jint)result;
-}
-
-static jint android_net_utils_removeHostRoutes(JNIEnv* env, jobject clazz, jstring ifname)
-{
-    int result;
-
-    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
-    result = ::ifc_remove_host_routes(nameStr);
-    env->ReleaseStringUTFChars(ifname, nameStr);
-    return (jint)result;
-}
-
-static jint android_net_utils_getDefaultRoute(JNIEnv* env, jobject clazz, jstring ifname)
-{
-    int result;
-
-    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
-    result = ::ifc_get_default_route(nameStr);
-    env->ReleaseStringUTFChars(ifname, nameStr);
-    return (jint)result;
-}
-
-static jint android_net_utils_removeDefaultRoute(JNIEnv* env, jobject clazz, jstring ifname)
-{
-    int result;
-
-    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
-    result = ::ifc_remove_default_route(nameStr);
-    env->ReleaseStringUTFChars(ifname, nameStr);
-    return (jint)result;
-}
-
-static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz, jstring ifname)
-{
-    int result;
-
-    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
-    result = ::ifc_reset_connections(nameStr);
-    env->ReleaseStringUTFChars(ifname, nameStr);
-    return (jint)result;
+    return (jboolean)(result == 0);
 }
 
 static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
 {
-    int result;
-    in_addr_t ipaddr, gateway, mask, dns1, dns2, server;
-    uint32_t lease;
-
-    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
-    result = ::dhcp_do_request(nameStr, &ipaddr, &gateway, &mask,
-                                        &dns1, &dns2, &server, &lease);
-    env->ReleaseStringUTFChars(ifname, nameStr);
-    if (result == 0 && dhcpInfoFieldIds.dhcpInfoClass != NULL) {
-        env->SetIntField(info, dhcpInfoFieldIds.ipaddress, ipaddr);
-        env->SetIntField(info, dhcpInfoFieldIds.gateway, gateway);
-        env->SetIntField(info, dhcpInfoFieldIds.netmask, mask);
-        env->SetIntField(info, dhcpInfoFieldIds.dns1, dns1);
-        env->SetIntField(info, dhcpInfoFieldIds.dns2, dns2);
-        env->SetIntField(info, dhcpInfoFieldIds.serverAddress, server);
-        env->SetIntField(info, dhcpInfoFieldIds.leaseDuration, lease);
-    }
-    return (jboolean)(result == 0);
+    return android_net_utils_runDhcpCommon(env, clazz, ifname, info, false);
 }
 
+static jboolean android_net_utils_runDhcpRenew(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
+{
+    return android_net_utils_runDhcpCommon(env, clazz, ifname, info, true);
+}
+
+
 static jboolean android_net_utils_stopDhcp(JNIEnv* env, jobject clazz, jstring ifname)
 {
     int result;
@@ -202,46 +202,6 @@
     return env->NewStringUTF(::dhcp_get_errmsg());
 }
 
-static jboolean android_net_utils_configureInterface(JNIEnv* env,
-        jobject clazz,
-        jstring ifname,
-        jint ipaddr,
-        jint mask,
-        jint gateway,
-        jint dns1,
-        jint dns2)
-{
-    int result;
-    uint32_t lease;
-
-    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
-    result = ::ifc_configure(nameStr, ipaddr, mask, gateway, dns1, dns2);
-    env->ReleaseStringUTFChars(ifname, nameStr);
-    return (jboolean)(result == 0);
-}
-
-static jboolean android_net_utils_runDhcpRenew(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
-{
-    int result = -1;
-    in_addr_t ipaddr, gateway, mask, dns1, dns2, server;
-    uint32_t lease;
-
-    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
-    result = ::dhcp_do_request_renew(nameStr, &ipaddr, &gateway, &mask,
-            &dns1, &dns2, &server, &lease);
-    env->ReleaseStringUTFChars(ifname, nameStr);
-    if (result == 0 && dhcpInfoFieldIds.dhcpInfoClass != NULL) {
-        env->SetIntField(info, dhcpInfoFieldIds.ipaddress, ipaddr);
-        env->SetIntField(info, dhcpInfoFieldIds.gateway, gateway);
-        env->SetIntField(info, dhcpInfoFieldIds.netmask, mask);
-        env->SetIntField(info, dhcpInfoFieldIds.dns1, dns1);
-        env->SetIntField(info, dhcpInfoFieldIds.dns2, dns2);
-        env->SetIntField(info, dhcpInfoFieldIds.serverAddress, server);
-        env->SetIntField(info, dhcpInfoFieldIds.leaseDuration, lease);
-    }
-
-    return (jboolean)(result == 0);
-}
 // ----------------------------------------------------------------------------
 
 /*
@@ -252,18 +212,12 @@
 
     { "enableInterface", "(Ljava/lang/String;)I",  (void *)android_net_utils_enableInterface },
     { "disableInterface", "(Ljava/lang/String;)I",  (void *)android_net_utils_disableInterface },
-    { "addRoute", "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;)I",
-       (void *)android_net_utils_addRoute },
-    { "removeHostRoutes", "(Ljava/lang/String;)I",  (void *)android_net_utils_removeHostRoutes },
-    { "getDefaultRoute", "(Ljava/lang/String;)I",  (void *)android_net_utils_getDefaultRoute },
-    { "removeDefaultRoute", "(Ljava/lang/String;)I",  (void *)android_net_utils_removeDefaultRoute },
-    { "resetConnections", "(Ljava/lang/String;)I",  (void *)android_net_utils_resetConnections },
-    { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z",  (void *)android_net_utils_runDhcp },
+    { "resetConnections", "(Ljava/lang/String;I)I",  (void *)android_net_utils_resetConnections },
+    { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z",  (void *)android_net_utils_runDhcp },
+    { "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z",  (void *)android_net_utils_runDhcpRenew },
     { "stopDhcp", "(Ljava/lang/String;)Z",  (void *)android_net_utils_stopDhcp },
     { "releaseDhcpLease", "(Ljava/lang/String;)Z",  (void *)android_net_utils_releaseDhcpLease },
-    { "configureNative", "(Ljava/lang/String;IIIII)Z",  (void *)android_net_utils_configureInterface },
     { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
-    { "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z",  (void *)android_net_utils_runDhcpRenew}
 };
 
 int register_android_net_NetworkUtils(JNIEnv* env)
@@ -271,16 +225,15 @@
     jclass netutils = env->FindClass(NETUTILS_PKG_NAME);
     LOG_FATAL_IF(netutils == NULL, "Unable to find class " NETUTILS_PKG_NAME);
 
-    dhcpInfoFieldIds.dhcpInfoClass = env->FindClass("android/net/DhcpInfo");
-    if (dhcpInfoFieldIds.dhcpInfoClass != NULL) {
-        dhcpInfoFieldIds.constructorId = env->GetMethodID(dhcpInfoFieldIds.dhcpInfoClass, "<init>", "()V");
-        dhcpInfoFieldIds.ipaddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "ipAddress", "I");
-        dhcpInfoFieldIds.gateway = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "gateway", "I");
-        dhcpInfoFieldIds.netmask = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "netmask", "I");
-        dhcpInfoFieldIds.dns1 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns1", "I");
-        dhcpInfoFieldIds.dns2 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns2", "I");
-        dhcpInfoFieldIds.serverAddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "serverAddress", "I");
-        dhcpInfoFieldIds.leaseDuration = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "leaseDuration", "I");
+    dhcpInfoInternalFieldIds.dhcpInfoInternalClass = env->FindClass("android/net/DhcpInfoInternal");
+    if (dhcpInfoInternalFieldIds.dhcpInfoInternalClass != NULL) {
+        dhcpInfoInternalFieldIds.constructorId = env->GetMethodID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "<init>", "()V");
+        dhcpInfoInternalFieldIds.ipaddress = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "ipAddress", "Ljava/lang/String;");
+        dhcpInfoInternalFieldIds.prefixLength = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "prefixLength", "I");
+        dhcpInfoInternalFieldIds.dns1 = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "dns1", "Ljava/lang/String;");
+        dhcpInfoInternalFieldIds.dns2 = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "dns2", "Ljava/lang/String;");
+        dhcpInfoInternalFieldIds.serverAddress = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "serverAddress", "Ljava/lang/String;");
+        dhcpInfoInternalFieldIds.leaseDuration = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "leaseDuration", "I");
     }
 
     return AndroidRuntime::registerNativeMethods(env,
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
new file mode 100644
index 0000000..e3b6b5f
--- /dev/null
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -0,0 +1,200 @@
+/*
+ * 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;
+
+import android.net.LinkProperties;
+import android.net.RouteInfo;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+import java.net.InetAddress;
+
+public class LinkPropertiesTest extends TestCase {
+    private static String ADDRV4 = "75.208.6.1";
+    private static String ADDRV6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+    private static String DNS1 = "75.208.7.1";
+    private static String DNS2 = "69.78.7.1";
+    private static String GATEWAY1 = "75.208.8.1";
+    private static String GATEWAY2 = "69.78.8.1";
+    private static String NAME = "qmi0";
+
+    @SmallTest
+    public void testEqualsNull() {
+        LinkProperties source = new LinkProperties();
+        LinkProperties target = new LinkProperties();
+
+        assertFalse(source == target);
+        assertTrue(source.equals(target));
+        assertTrue(source.hashCode() == target.hashCode());
+    }
+
+    @SmallTest
+    public void testEqualsSameOrder() {
+        try {
+            LinkProperties source = new LinkProperties();
+            source.setInterfaceName(NAME);
+            // set 2 link addresses
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            // set 2 dnses
+            source.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            source.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            // set 2 gateways
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+
+            LinkProperties target = new LinkProperties();
+
+            // All fields are same
+            target.setInterfaceName(NAME);
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+
+            assertTrue(source.equals(target));
+            assertTrue(source.hashCode() == target.hashCode());
+
+            target.clear();
+            // change Interface Name
+            target.setInterfaceName("qmi1");
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+            assertFalse(source.equals(target));
+
+            target.clear();
+            target.setInterfaceName(NAME);
+            // change link addresses
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress("75.208.6.2"), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+            assertFalse(source.equals(target));
+
+            target.clear();
+            target.setInterfaceName(NAME);
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            // change dnses
+            target.addDns(NetworkUtils.numericToInetAddress("75.208.7.2"));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+            assertFalse(source.equals(target));
+
+            target.clear();
+            target.setInterfaceName(NAME);
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            // change gateway
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress("75.208.8.2")));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+            assertFalse(source.equals(target));
+
+        } catch (Exception e) {
+            throw new RuntimeException(e.toString());
+            //fail();
+        }
+    }
+
+    @SmallTest
+    public void testEqualsDifferentOrder() {
+        try {
+            LinkProperties source = new LinkProperties();
+            source.setInterfaceName(NAME);
+            // set 2 link addresses
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            // set 2 dnses
+            source.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            source.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            // set 2 gateways
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+
+            LinkProperties target = new LinkProperties();
+            // Exchange order
+            target.setInterfaceName(NAME);
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+
+            assertTrue(source.equals(target));
+            assertTrue(source.hashCode() == target.hashCode());
+        } catch (Exception e) {
+            fail();
+        }
+    }
+
+    @SmallTest
+    public void testEqualsDuplicated() {
+        try {
+            LinkProperties source = new LinkProperties();
+            // set 3 link addresses, eg, [A, A, B]
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+
+            LinkProperties target = new LinkProperties();
+            // set 3 link addresses, eg, [A, B, B]
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+
+            assertTrue(source.equals(target));
+            assertTrue(source.hashCode() == target.hashCode());
+        } catch (Exception e) {
+            fail();
+        }
+    }
+
+}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 6cd3e9e..335d3fe 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -16,29 +16,37 @@
 
 package com.android.server;
 
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.Context;
+import android.bluetooth.BluetoothTetheringDataTracker;
 import android.content.ContentResolver;
-import android.content.ContextWrapper;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
+import android.database.ContentObserver;
 import android.net.ConnectivityManager;
+import android.net.DummyDataStateTracker;
+import android.net.EthernetDataTracker;
 import android.net.IConnectivityManager;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.LinkProperties.CompareResult;
 import android.net.MobileDataStateTracker;
+import android.net.NetworkConfig;
 import android.net.NetworkInfo;
 import android.net.NetworkStateTracker;
 import android.net.NetworkUtils;
+import android.net.Proxy;
+import android.net.ProxyProperties;
+import android.net.RouteInfo;
+import android.net.vpn.VpnManager;
 import android.net.wifi.WifiStateTracker;
-import android.net.wimax.WimaxManagerConstants;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
-import android.os.INetworkManagementService;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
@@ -46,29 +54,31 @@
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Slog;
+
 import com.android.internal.telephony.Phone;
 import com.android.server.connectivity.Tethering;
-import dalvik.system.DexClassLoader;
+
 import java.io.FileDescriptor;
+import java.io.FileWriter;
+import java.io.IOException;
 import java.io.PrintWriter;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.InvocationTargetException;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.GregorianCalendar;
 import java.util.List;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-
 
 /**
  * @hide
  */
 public class ConnectivityService extends IConnectivityManager.Stub {
 
-    private static final boolean DBG = false;
+    private static final boolean DBG = true;
+    private static final boolean VDBG = true;
     private static final String TAG = "ConnectivityService";
 
     // how long to wait before switching back to a radio's default network
@@ -77,6 +87,9 @@
     private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
             "android.telephony.apn-restore";
 
+    // used in recursive route setting to add gateways for the host for which
+    // a host route was requested.
+    private static final int MAX_HOSTROUTE_CYCLE_COUNT = 10;
 
     private Tethering mTethering;
     private boolean mTetheringConfigValid = false;
@@ -89,11 +102,18 @@
     private NetworkStateTracker mNetTrackers[];
 
     /**
+     * The link properties that define the current links
+     */
+    private LinkProperties mCurrentLinkProperties[];
+
+    /**
      * A per Net list of the PID's that requested access to the net
      * used both as a refcount and for per-PID DNS selection
      */
     private List mNetRequestersPids[];
 
+    private WifiWatchdogService mWifiWatchdogService;
+
     // priority order of the nettrackers
     // (excluding dynamically set mNetworkPreference)
     // TODO - move mNetworkTypePreference into this
@@ -113,6 +133,8 @@
     private boolean mTestMode;
     private static ConnectivityService sServiceInstance;
 
+    private AtomicBoolean mBackgroundDataEnabled = new AtomicBoolean(true);
+
     private INetworkManagementService mNetd;
 
     private static final int ENABLED  = 1;
@@ -173,41 +195,60 @@
     private static final int EVENT_SET_MOBILE_DATA =
             MAX_NETWORK_STATE_TRACKER_EVENT + 7;
 
+    /**
+     * used internally to clear a wakelock when transitioning
+     * from one net to another
+     */
+    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;
+
+    /**
+     * used internally to set external dependency met/unmet
+     * arg1 = ENABLED (met) or DISABLED (unmet)
+     * arg2 = NetworkType
+     */
+    private static final int EVENT_SET_DEPENDENCY_MET =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 10;
+
     private Handler mHandler;
 
     // list of DeathRecipients used to make sure features are turned off when
     // a process dies
-    private List mFeatureUsers;
+    private List<FeatureUser> mFeatureUsers;
 
     private boolean mSystemReady;
     private Intent mInitialBroadcast;
 
+    private PowerManager.WakeLock mNetTransitionWakeLock;
+    private String mNetTransitionWakeLockCausedBy = "";
+    private int mNetTransitionWakeLockSerialNumber;
+    private int mNetTransitionWakeLockTimeout;
+
+    private InetAddress mDefaultDns;
+
+    // this collection is used to refcount the added routes - if there are none left
+    // it's time to remove the route from the route table
+    private Collection<RouteInfo> mAddedRoutes = new ArrayList<RouteInfo>();
+
     // used in DBG mode to track inet condition reports
     private static final int INET_CONDITION_LOG_MAX_SIZE = 15;
     private ArrayList mInetLog;
 
-    private static class NetworkAttributes {
-        /**
-         * Class for holding settings read from resources.
-         */
-        public String mName;
-        public int mType;
-        public int mRadio;
-        public int mPriority;
-        public NetworkInfo.State mLastState;
-        public NetworkAttributes(String init) {
-            String fragments[] = init.split(",");
-            mName = fragments[0].toLowerCase();
-            mType = Integer.parseInt(fragments[1]);
-            mRadio = Integer.parseInt(fragments[2]);
-            mPriority = Integer.parseInt(fragments[3]);
-            mLastState = NetworkInfo.State.UNKNOWN;
-        }
-        public boolean isDefault() {
-            return (mType == mRadio);
-        }
-    }
-    NetworkAttributes[] mNetAttributes;
+    // 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;
+
+    NetworkConfig[] mNetConfigs;
     int mNetworksDefined;
 
     private static class RadioAttributes {
@@ -221,69 +262,64 @@
     }
     RadioAttributes[] mRadioAttributes;
 
-    private static class ConnectivityThread extends Thread {
-        private Context mContext;
+    // the set of network types that can only be enabled by system/sig apps
+    List mProtectedNetworks;
 
-        private ConnectivityThread(Context context) {
-            super("ConnectivityThread");
-            mContext = context;
+    public static synchronized ConnectivityService getInstance(Context context) {
+        if (sServiceInstance == null) {
+            sServiceInstance = new ConnectivityService(context);
         }
-
-        @Override
-        public void run() {
-            Looper.prepare();
-            synchronized (this) {
-                sServiceInstance = new ConnectivityService(mContext);
-                notifyAll();
-            }
-            Looper.loop();
-        }
-
-        public static ConnectivityService getServiceInstance(Context context) {
-            ConnectivityThread thread = new ConnectivityThread(context);
-            thread.start();
-
-            synchronized (thread) {
-                while (sServiceInstance == null) {
-                    try {
-                        // Wait until sServiceInstance has been initialized.
-                        thread.wait();
-                    } catch (InterruptedException ignore) {
-                        Slog.e(TAG,
-                            "Unexpected InterruptedException while waiting"+
-                            " for ConnectivityService thread");
-                    }
-                }
-            }
-
-            return sServiceInstance;
-        }
-    }
-
-    public static ConnectivityService getInstance(Context context) {
-        return ConnectivityThread.getServiceInstance(context);
+        return sServiceInstance;
     }
 
     private ConnectivityService(Context context) {
-        if (DBG) Slog.v(TAG, "ConnectivityService starting up");
+        if (DBG) log("ConnectivityService starting up");
+
+        HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
+        handlerThread.start();
+        mHandler = new MyHandler(handlerThread.getLooper());
+
+        mBackgroundDataEnabled.set(Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.BACKGROUND_DATA, 1) == 1);
 
         // setup our unique device name
-        String id = Settings.Secure.getString(context.getContentResolver(),
-                Settings.Secure.ANDROID_ID);
-        if (id != null && id.length() > 0) {
-            String name = new String("android_").concat(id);
-            SystemProperties.set("net.hostname", name);
+        if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) {
+            String id = Settings.Secure.getString(context.getContentResolver(),
+                    Settings.Secure.ANDROID_ID);
+            if (id != null && id.length() > 0) {
+                String name = new String("android_").concat(id);
+                SystemProperties.set("net.hostname", name);
+            }
+        }
+
+        // read our default dns server ip
+        String dns = Settings.Secure.getString(context.getContentResolver(),
+                Settings.Secure.DEFAULT_DNS_SERVER);
+        if (dns == null || dns.length() == 0) {
+            dns = context.getResources().getString(
+                    com.android.internal.R.string.config_default_dns_server);
+        }
+        try {
+            mDefaultDns = NetworkUtils.numericToInetAddress(dns);
+        } catch (IllegalArgumentException e) {
+            loge("Error setting defaultDns using " + dns);
         }
 
         mContext = context;
+
+        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+        mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_networkTransitionTimeout);
+
         mNetTrackers = new NetworkStateTracker[
                 ConnectivityManager.MAX_NETWORK_TYPE+1];
-        mHandler = new MyHandler();
+        mCurrentLinkProperties = new LinkProperties[ConnectivityManager.MAX_NETWORK_TYPE+1];
 
         mNetworkPreference = getPersistedNetworkPreference();
 
         mRadioAttributes = new RadioAttributes[ConnectivityManager.MAX_RADIO_TYPE+1];
-        mNetAttributes = new NetworkAttributes[ConnectivityManager.MAX_NETWORK_TYPE+1];
+        mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1];
 
         // Load device network attributes from resources
         String[] raStrings = context.getResources().getStringArray(
@@ -291,11 +327,11 @@
         for (String raString : raStrings) {
             RadioAttributes r = new RadioAttributes(raString);
             if (r.mType > ConnectivityManager.MAX_RADIO_TYPE) {
-                Slog.e(TAG, "Error in radioAttributes - ignoring attempt to define type " + r.mType);
+                loge("Error in radioAttributes - ignoring attempt to define type " + r.mType);
                 continue;
             }
             if (mRadioAttributes[r.mType] != null) {
-                Slog.e(TAG, "Error in radioAttributes - ignoring attempt to redefine type " +
+                loge("Error in radioAttributes - ignoring attempt to redefine type " +
                         r.mType);
                 continue;
             }
@@ -306,29 +342,40 @@
                 com.android.internal.R.array.networkAttributes);
         for (String naString : naStrings) {
             try {
-                NetworkAttributes n = new NetworkAttributes(naString);
-                if (n.mType > ConnectivityManager.MAX_NETWORK_TYPE) {
-                    Slog.e(TAG, "Error in networkAttributes - ignoring attempt to define type " +
-                            n.mType);
+                NetworkConfig n = new NetworkConfig(naString);
+                if (n.type > ConnectivityManager.MAX_NETWORK_TYPE) {
+                    loge("Error in networkAttributes - ignoring attempt to define type " +
+                            n.type);
                     continue;
                 }
-                if (mNetAttributes[n.mType] != null) {
-                    Slog.e(TAG, "Error in networkAttributes - ignoring attempt to redefine type " +
-                            n.mType);
+                if (mNetConfigs[n.type] != null) {
+                    loge("Error in networkAttributes - ignoring attempt to redefine type " +
+                            n.type);
                     continue;
                 }
-                if (mRadioAttributes[n.mRadio] == null) {
-                    Slog.e(TAG, "Error in networkAttributes - ignoring attempt to use undefined " +
-                            "radio " + n.mRadio + " in network type " + n.mType);
+                if (mRadioAttributes[n.radio] == null) {
+                    loge("Error in networkAttributes - ignoring attempt to use undefined " +
+                            "radio " + n.radio + " in network type " + n.type);
                     continue;
                 }
-                mNetAttributes[n.mType] = n;
+                mNetConfigs[n.type] = n;
                 mNetworksDefined++;
             } catch(Exception e) {
                 // ignore it - leave the entry null
             }
         }
 
+        mProtectedNetworks = new ArrayList<Integer>();
+        int[] protectedNetworks = context.getResources().getIntArray(
+                com.android.internal.R.array.config_protectedNetworks);
+        for (int p : protectedNetworks) {
+            if ((mNetConfigs[p] != null) && (mProtectedNetworks.contains(p) == false)) {
+                mProtectedNetworks.add(p);
+            } else {
+                if (DBG) loge("Ignoring protectedNetwork " + p);
+            }
+        }
+
         // high priority first
         mPriorityList = new int[mNetworksDefined];
         {
@@ -336,16 +383,16 @@
             int currentLowest = 0;
             int nextLowest = 0;
             while (insertionPoint > -1) {
-                for (NetworkAttributes na : mNetAttributes) {
+                for (NetworkConfig na : mNetConfigs) {
                     if (na == null) continue;
-                    if (na.mPriority < currentLowest) continue;
-                    if (na.mPriority > currentLowest) {
-                        if (na.mPriority < nextLowest || nextLowest == 0) {
-                            nextLowest = na.mPriority;
+                    if (na.priority < currentLowest) continue;
+                    if (na.priority > currentLowest) {
+                        if (na.priority < nextLowest || nextLowest == 0) {
+                            nextLowest = na.priority;
                         }
                         continue;
                     }
-                    mPriorityList[insertionPoint--] = na.mType;
+                    mPriorityList[insertionPoint--] = na.type;
                 }
                 currentLowest = nextLowest;
                 nextLowest = 0;
@@ -357,7 +404,7 @@
             mNetRequestersPids[i] = new ArrayList();
         }
 
-        mFeatureUsers = new ArrayList();
+        mFeatureUsers = new ArrayList<FeatureUser>();
 
         mNumDnsEntries = 0;
 
@@ -370,146 +417,65 @@
          * the number of different network types is not going
          * to change very often.
          */
-        boolean noMobileData = !getMobileDataEnabled();
         for (int netType : mPriorityList) {
-            switch (mNetAttributes[netType].mRadio) {
+            switch (mNetConfigs[netType].radio) {
             case ConnectivityManager.TYPE_WIFI:
-                if (DBG) Slog.v(TAG, "Starting Wifi Service.");
-                WifiStateTracker wst = new WifiStateTracker(context, mHandler);
-                WifiService wifiService = new WifiService(context, wst);
+                if (DBG) log("Starting Wifi Service.");
+                WifiStateTracker wst = new WifiStateTracker();
+                WifiService wifiService = new WifiService(context);
                 ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
-                wifiService.startWifi();
+                wifiService.checkAndStartWifi();
                 mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
-                wst.startMonitoring();
+                wst.startMonitoring(context, mHandler);
+
+                //TODO: as part of WWS refactor, create only when needed
+                mWifiWatchdogService = new WifiWatchdogService(context);
 
                 break;
             case ConnectivityManager.TYPE_MOBILE:
-                mNetTrackers[netType] = new MobileDataStateTracker(context, mHandler,
-                    netType, mNetAttributes[netType].mName);
-                mNetTrackers[netType].startMonitoring();
-                if (noMobileData) {
-                    if (DBG) Slog.d(TAG, "tearing down Mobile networks due to setting");
-                    mNetTrackers[netType].teardown();
-                }
+                mNetTrackers[netType] = new MobileDataStateTracker(netType,
+                        mNetConfigs[netType].name);
+                mNetTrackers[netType].startMonitoring(context, mHandler);
                 break;
-            case ConnectivityManager.TYPE_WIMAX:
-                NetworkStateTracker nst = makeWimaxStateTracker();
-                if (nst != null) {
-                    nst.startMonitoring();
-                }
-                mNetTrackers[netType] = nst;
-                if (noMobileData) {
-                    if (DBG) Slog.d(TAG, "tearing down WiMAX networks due to setting");
-                    mNetTrackers[netType].teardown();
-                }
+            case ConnectivityManager.TYPE_DUMMY:
+                mNetTrackers[netType] = new DummyDataStateTracker(netType,
+                        mNetConfigs[netType].name);
+                mNetTrackers[netType].startMonitoring(context, mHandler);
+                break;
+            case ConnectivityManager.TYPE_BLUETOOTH:
+                mNetTrackers[netType] = BluetoothTetheringDataTracker.getInstance();
+                mNetTrackers[netType].startMonitoring(context, mHandler);
+                break;
+            case ConnectivityManager.TYPE_ETHERNET:
+                mNetTrackers[netType] = EthernetDataTracker.getInstance();
+                mNetTrackers[netType].startMonitoring(context, mHandler);
                 break;
             default:
-                Slog.e(TAG, "Trying to create a DataStateTracker for an unknown radio type " +
-                        mNetAttributes[netType].mRadio);
+                loge("Trying to create a DataStateTracker for an unknown radio type " +
+                        mNetConfigs[netType].radio);
                 continue;
             }
+            mCurrentLinkProperties[netType] = null;
         }
 
         mTethering = new Tethering(mContext, mHandler.getLooper());
-        mTetheringConfigValid = (((mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] != null) ||
-                                  !mTethering.isDunRequired()) &&
-                                 (mTethering.getTetherableUsbRegexs().length != 0 ||
-                                  mTethering.getTetherableWifiRegexs().length != 0) &&
-                                 mTethering.getUpstreamIfaceRegexs().length != 0);
+        mTetheringConfigValid = ((mTethering.getTetherableUsbRegexs().length != 0 ||
+                                  mTethering.getTetherableWifiRegexs().length != 0 ||
+                                  mTethering.getTetherableBluetoothRegexs().length != 0) &&
+                                 mTethering.getUpstreamIfaceTypes().length != 0);
 
         if (DBG) {
             mInetLog = new ArrayList();
         }
+
+        mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
+        mSettingsObserver.observe(mContext);
+
+        loadGlobalProxy();
+
+        VpnManager.startVpnService(context);
     }
 
-    private NetworkStateTracker makeWimaxStateTracker() {
-        //Initialize Wimax
-        DexClassLoader wimaxClassLoader;
-        Class wimaxStateTrackerClass = null;
-        Class wimaxServiceClass = null;
-        Class wimaxManagerClass;
-        String wimaxJarLocation;
-        String wimaxLibLocation;
-        String wimaxManagerClassName;
-        String wimaxServiceClassName;
-        String wimaxStateTrackerClassName;
-
-        NetworkStateTracker wimaxStateTracker = null;
-
-        boolean isWimaxEnabled = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_wimaxEnabled);
-
-        if (isWimaxEnabled) {
-            try {
-                wimaxJarLocation = mContext.getResources().getString(
-                        com.android.internal.R.string.config_wimaxServiceJarLocation);
-                wimaxLibLocation = mContext.getResources().getString(
-                        com.android.internal.R.string.config_wimaxNativeLibLocation);
-                wimaxManagerClassName = mContext.getResources().getString(
-                        com.android.internal.R.string.config_wimaxManagerClassname);
-                wimaxServiceClassName = mContext.getResources().getString(
-                        com.android.internal.R.string.config_wimaxServiceClassname);
-                wimaxStateTrackerClassName = mContext.getResources().getString(
-                        com.android.internal.R.string.config_wimaxStateTrackerClassname);
-
-                wimaxClassLoader =  new DexClassLoader(wimaxJarLocation,
-                        new ContextWrapper(mContext).getCacheDir().getAbsolutePath(),
-                        wimaxLibLocation,ClassLoader.getSystemClassLoader());
-
-                try {
-                    wimaxManagerClass = wimaxClassLoader.loadClass(wimaxManagerClassName);
-                    wimaxStateTrackerClass = wimaxClassLoader.loadClass(wimaxStateTrackerClassName);
-                    wimaxServiceClass = wimaxClassLoader.loadClass(wimaxServiceClassName);
-                } catch (ClassNotFoundException ex) {
-                    ex.printStackTrace();
-                    return null;
-                }
-            } catch(Resources.NotFoundException ex) {
-                Slog.e(TAG, "Wimax Resources does not exist!!! ");
-                return null;
-            }
-
-            try {
-                Slog.v(TAG, "Starting Wimax Service... ");
-
-                Constructor wmxStTrkrConst = wimaxStateTrackerClass.getConstructor
-                        (new Class[] {Context.class,Handler.class});
-                wimaxStateTracker = (NetworkStateTracker)wmxStTrkrConst.newInstance(mContext,mHandler);
-
-                Constructor wmxSrvConst = wimaxServiceClass.getDeclaredConstructor
-                        (new Class[] {Context.class,wimaxStateTrackerClass});
-                wmxSrvConst.setAccessible(true);
-                IBinder svcInvoker = (IBinder) wmxSrvConst.newInstance(mContext,wimaxStateTracker);
-                wmxSrvConst.setAccessible(false);
-
-                ServiceManager.addService(WimaxManagerConstants.WIMAX_SERVICE, svcInvoker);
-
-            } catch(ClassCastException ex) {
-                ex.printStackTrace();
-                return null;
-            } catch (NoSuchMethodException ex) {
-                ex.printStackTrace();
-                return null;
-            } catch (InstantiationException ex) {
-                ex.printStackTrace();
-                return null;
-            } catch(IllegalAccessException ex) {
-                ex.printStackTrace();
-                return null;
-            } catch(InvocationTargetException ex) {
-                ex.printStackTrace();
-                return null;
-            } catch(Exception ex) {
-                ex.printStackTrace();
-                return null;
-            }
-        } else {
-            Slog.e(TAG, "Wimax is not enabled or not added to the network attributes!!! ");
-            return null;
-        }
-
-        return wimaxStateTracker;
-    }
 
     /**
      * Sets the preferred network.
@@ -532,8 +498,8 @@
 
     private void handleSetNetworkPreference(int preference) {
         if (ConnectivityManager.isNetworkTypeValid(preference) &&
-                mNetAttributes[preference] != null &&
-                mNetAttributes[preference].isDefault()) {
+                mNetConfigs[preference] != null &&
+                mNetConfigs[preference].isDefault()) {
             if (mNetworkPreference != preference) {
                 final ContentResolver cr = mContext.getContentResolver();
                 Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, preference);
@@ -575,8 +541,7 @@
             if (t != mNetworkPreference && mNetTrackers[t] != null &&
                     mNetTrackers[t].getNetworkInfo().isConnected()) {
                 if (DBG) {
-                    Slog.d(TAG, "tearing down " +
-                            mNetTrackers[t].getNetworkInfo() +
+                    log("tearing down " + mNetTrackers[t].getNetworkInfo() +
                             " in enforcePreference");
                 }
                 teardown(mNetTrackers[t]);
@@ -601,11 +566,7 @@
      * active
      */
     public NetworkInfo getActiveNetworkInfo() {
-        enforceAccessPermission();
-        if (mActiveDefaultNetwork != -1) {
-            return mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
-        }
-        return null;
+        return getNetworkInfo(mActiveDefaultNetwork);
     }
 
     public NetworkInfo getNetworkInfo(int networkType) {
@@ -628,6 +589,27 @@
         return result;
     }
 
+    /**
+     * Return LinkProperties for the active (i.e., connected) default
+     * network interface.  It is assumed that at most one default network
+     * is active at a time. If more than one is active, it is indeterminate
+     * which will be returned.
+     * @return the ip properties for the active network, or {@code null} if
+     * none is active
+     */
+    public LinkProperties getActiveLinkProperties() {
+        return getLinkProperties(mActiveDefaultNetwork);
+    }
+
+    public LinkProperties getLinkProperties(int networkType) {
+        enforceAccessPermission();
+        if (ConnectivityManager.isNetworkTypeValid(networkType)) {
+            NetworkStateTracker t = mNetTrackers[networkType];
+            if (t != null) return t.getLinkProperties();
+        }
+        return null;
+    }
+
     public boolean setRadios(boolean turnOn) {
         boolean result = true;
         enforceChangePermission();
@@ -682,19 +664,33 @@
         }
 
         public void binderDied() {
-            Slog.d(TAG, "ConnectivityService FeatureUser binderDied(" +
+            log("ConnectivityService FeatureUser binderDied(" +
                     mNetworkType + ", " + mFeature + ", " + mBinder + "), created " +
                     (System.currentTimeMillis() - mCreateTime) + " mSec ago");
             stopUsingNetworkFeature(this, false);
         }
 
         public void expire() {
-            Slog.d(TAG, "ConnectivityService FeatureUser expire(" +
+            log("ConnectivityService FeatureUser expire(" +
                     mNetworkType + ", " + mFeature + ", " + mBinder +"), created " +
                     (System.currentTimeMillis() - mCreateTime) + " mSec ago");
             stopUsingNetworkFeature(this, false);
         }
 
+        public boolean isSameUser(FeatureUser u) {
+            if (u == null) return false;
+
+            return isSameUser(u.mPid, u.mUid, u.mNetworkType, u.mFeature);
+        }
+
+        public boolean isSameUser(int pid, int uid, int networkType, String feature) {
+            if ((mPid == pid) && (mUid == uid) && (mNetworkType == networkType) &&
+                TextUtils.equals(mFeature, feature)) {
+                return true;
+            }
+            return false;
+        }
+
         public String toString() {
             return "FeatureUser("+mNetworkType+","+mFeature+","+mPid+","+mUid+"), created " +
                     (System.currentTimeMillis() - mCreateTime) + " mSec ago";
@@ -705,12 +701,11 @@
     public int startUsingNetworkFeature(int networkType, String feature,
             IBinder binder) {
         if (DBG) {
-            Slog.d(TAG, "startUsingNetworkFeature for net " + networkType +
-                    ": " + feature);
+            log("startUsingNetworkFeature for net " + networkType + ": " + feature);
         }
         enforceChangePermission();
         if (!ConnectivityManager.isNetworkTypeValid(networkType) ||
-                mNetAttributes[networkType] == null) {
+                mNetConfigs[networkType] == null) {
             return Phone.APN_REQUEST_FAILED;
         }
 
@@ -719,33 +714,81 @@
         // TODO - move this into the MobileDataStateTracker
         int usedNetworkType = networkType;
         if(networkType == ConnectivityManager.TYPE_MOBILE) {
-            if (!getMobileDataEnabled()) {
-                if (DBG) Slog.d(TAG, "requested special network with data disabled - rejected");
-                return Phone.APN_TYPE_NOT_AVAILABLE;
-            }
-            if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
-                usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
-            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
-                usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
-            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) {
-                usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
-            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
-                usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
+            usedNetworkType = convertFeatureToNetworkType(feature);
+            if (usedNetworkType < 0) {
+                Slog.e(TAG, "Can't match any netTracker!");
+                usedNetworkType = networkType;
             }
         }
+
+        if (mProtectedNetworks.contains(usedNetworkType)) {
+            enforceConnectivityInternalPermission();
+        }
+
         NetworkStateTracker network = mNetTrackers[usedNetworkType];
         if (network != null) {
+            Integer currentPid = new Integer(getCallingPid());
             if (usedNetworkType != networkType) {
-                Integer currentPid = new Integer(getCallingPid());
-
                 NetworkStateTracker radio = mNetTrackers[networkType];
                 NetworkInfo ni = network.getNetworkInfo();
 
                 if (ni.isAvailable() == false) {
-                    if (DBG) Slog.d(TAG, "special network not available");
-                    return Phone.APN_TYPE_NOT_AVAILABLE;
+                    if (DBG) log("special network not available");
+                    if (!TextUtils.equals(feature,Phone.FEATURE_ENABLE_DUN_ALWAYS)) {
+                        return Phone.APN_TYPE_NOT_AVAILABLE;
+                    } else {
+                        // else make the attempt anyway - probably giving REQUEST_STARTED below
+                    }
                 }
 
+                int restoreTimer = getRestoreDefaultNetworkDelay(usedNetworkType);
+
+                synchronized(this) {
+                    boolean addToList = true;
+                    if (restoreTimer < 0) {
+                        // In case there is no timer is specified for the feature,
+                        // make sure we don't add duplicate entry with the same request.
+                        for (FeatureUser u : mFeatureUsers) {
+                            if (u.isSameUser(f)) {
+                                // Duplicate user is found. Do not add.
+                                addToList = false;
+                                break;
+                            }
+                        }
+                    }
+
+                    if (addToList) mFeatureUsers.add(f);
+                    if (!mNetRequestersPids[usedNetworkType].contains(currentPid)) {
+                        // this gets used for per-pid dns when connected
+                        mNetRequestersPids[usedNetworkType].add(currentPid);
+                    }
+                }
+
+                if (restoreTimer >= 0) {
+                    mHandler.sendMessageDelayed(
+                            mHandler.obtainMessage(EVENT_RESTORE_DEFAULT_NETWORK, f), restoreTimer);
+                }
+
+                if ((ni.isConnectedOrConnecting() == true) &&
+                        !network.isTeardownRequested()) {
+                    if (ni.isConnected() == true) {
+                        // add the pid-specific dns
+                        handleDnsConfigurationChange(networkType);
+                        if (DBG) log("special network already active");
+                        return Phone.APN_ALREADY_ACTIVE;
+                    }
+                    if (DBG) log("special network already connecting");
+                    return Phone.APN_REQUEST_STARTED;
+                }
+
+                // check if the radio in play can make another contact
+                // assume if cannot for now
+
+                if (DBG) log("reconnecting to special network");
+                network.reconnect();
+                return Phone.APN_REQUEST_STARTED;
+            } else {
+                // need to remember this unsupported request so we respond appropriately on stop
                 synchronized(this) {
                     mFeatureUsers.add(f);
                     if (!mNetRequestersPids[usedNetworkType].contains(currentPid)) {
@@ -753,37 +796,7 @@
                         mNetRequestersPids[usedNetworkType].add(currentPid);
                     }
                 }
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_RESTORE_DEFAULT_NETWORK,
-                        f), getRestoreDefaultNetworkDelay());
-
-
-                if ((ni.isConnectedOrConnecting() == true) &&
-                        !network.isTeardownRequested()) {
-                    if (ni.isConnected() == true) {
-                        // add the pid-specific dns
-                        handleDnsConfigurationChange(networkType);
-                        if (DBG) Slog.d(TAG, "special network already active");
-                        return Phone.APN_ALREADY_ACTIVE;
-                    }
-                    if (DBG) Slog.d(TAG, "special network already connecting");
-                    return Phone.APN_REQUEST_STARTED;
-                }
-
-                // check if the radio in play can make another contact
-                // assume if cannot for now
-
-                if (DBG) Slog.d(TAG, "reconnecting to special network");
-                network.reconnect();
-                return Phone.APN_REQUEST_STARTED;
-            } else {
-                synchronized(this) {
-                    mFeatureUsers.add(f);
-                }
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_RESTORE_DEFAULT_NETWORK,
-                        f), getRestoreDefaultNetworkDelay());
-
-                return network.startUsingNetworkFeature(feature,
-                        getCallingPid(), getCallingUid());
+                return -1;
             }
         }
         return Phone.APN_TYPE_NOT_AVAILABLE;
@@ -800,11 +813,9 @@
         boolean found = false;
 
         synchronized(this) {
-            for (int i = 0; i < mFeatureUsers.size() ; i++) {
-                u = (FeatureUser)mFeatureUsers.get(i);
-                if (uid == u.mUid && pid == u.mPid &&
-                        networkType == u.mNetworkType &&
-                        TextUtils.equals(feature, u.mFeature)) {
+            for (FeatureUser x : mFeatureUsers) {
+                if (x.isSameUser(pid, uid, networkType, feature)) {
+                    u = x;
                     found = true;
                     break;
                 }
@@ -815,7 +826,7 @@
             return stopUsingNetworkFeature(u, true);
         } else {
             // none found!
-            if (DBG) Slog.d(TAG, "ignoring stopUsingNetworkFeature - not a live request");
+            if (DBG) log("ignoring stopUsingNetworkFeature - not a live request");
             return 1;
         }
     }
@@ -830,7 +841,7 @@
         boolean callTeardown = false;  // used to carry our decision outside of sync block
 
         if (DBG) {
-            Slog.d(TAG, "stopUsingNetworkFeature for net " + networkType +
+            log("stopUsingNetworkFeature for net " + networkType +
                     ": " + feature);
         }
 
@@ -843,7 +854,7 @@
         synchronized(this) {
             // check if this process still has an outstanding start request
             if (!mFeatureUsers.contains(u)) {
-                if (DBG) Slog.d(TAG, "ignoring - this process has no outstanding requests");
+                if (DBG) log("ignoring - this process has no outstanding requests");
                 return 1;
             }
             u.unlinkDeathRecipient();
@@ -856,12 +867,9 @@
             // do not pay attention to duplicate requests - in effect the
             // API does not refcount and a single stop will counter multiple starts.
             if (ignoreDups == false) {
-                for (int i = 0; i < mFeatureUsers.size() ; i++) {
-                    FeatureUser x = (FeatureUser)mFeatureUsers.get(i);
-                    if (x.mUid == u.mUid && x.mPid == u.mPid &&
-                            x.mNetworkType == u.mNetworkType &&
-                            TextUtils.equals(x.mFeature, u.mFeature)) {
-                        if (DBG) Slog.d(TAG, "ignoring stopUsingNetworkFeature as dup is found");
+                for (FeatureUser x : mFeatureUsers) {
+                    if (x.isSameUser(u)) {
+                        if (DBG) log("ignoring stopUsingNetworkFeature as dup is found");
                         return 1;
                     }
                 }
@@ -870,19 +878,14 @@
             // TODO - move to MobileDataStateTracker
             int usedNetworkType = networkType;
             if (networkType == ConnectivityManager.TYPE_MOBILE) {
-                if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
-                    usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
-                } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
-                    usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
-                } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) {
-                    usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
-                } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
-                    usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
+                usedNetworkType = convertFeatureToNetworkType(feature);
+                if (usedNetworkType < 0) {
+                    usedNetworkType = networkType;
                 }
             }
             tracker =  mNetTrackers[usedNetworkType];
             if (tracker == null) {
-                if (DBG) Slog.d(TAG, "ignoring - no known tracker for net type " + usedNetworkType);
+                if (DBG) log("ignoring - no known tracker for net type " + usedNetworkType);
                 return -1;
             }
             if (usedNetworkType != networkType) {
@@ -890,20 +893,21 @@
                 mNetRequestersPids[usedNetworkType].remove(currentPid);
                 reassessPidDns(pid, true);
                 if (mNetRequestersPids[usedNetworkType].size() != 0) {
-                    if (DBG) Slog.d(TAG, "not tearing down special network - " +
+                    if (DBG) log("not tearing down special network - " +
                            "others still using it");
                     return 1;
                 }
                 callTeardown = true;
+            } else {
+                if (DBG) log("not a known feature - dropping");
             }
         }
-        if (DBG) Slog.d(TAG, "Doing network teardown");
+        if (DBG) log("Doing network teardown");
         if (callTeardown) {
             tracker.teardown();
             return 1;
         } else {
-            // do it the old fashioned way
-            return tracker.stopUsingNetworkFeature(feature, pid, uid);
+            return -1;
         }
     }
 
@@ -939,6 +943,10 @@
      */
     public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
         enforceChangePermission();
+        if (mProtectedNetworks.contains(networkType)) {
+            enforceConnectivityInternalPermission();
+        }
+
         if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
             return false;
         }
@@ -947,26 +955,110 @@
         if (tracker == null || !tracker.getNetworkInfo().isConnected() ||
                 tracker.isTeardownRequested()) {
             if (DBG) {
-                Slog.d(TAG, "requestRouteToHostAddress on down network " +
+                log("requestRouteToHostAddress on down network " +
                            "(" + networkType + ") - dropped");
             }
             return false;
         }
-
         try {
-            InetAddress inetAddress = InetAddress.getByAddress(hostAddress);
-            return tracker.requestRouteToHost(inetAddress);
-        } catch (UnknownHostException e) {
+            InetAddress addr = InetAddress.getByAddress(hostAddress);
+            LinkProperties lp = tracker.getLinkProperties();
+            return addRouteToAddress(lp, addr);
+        } catch (UnknownHostException e) {}
+        return false;
+    }
+
+    private boolean addRoute(LinkProperties p, RouteInfo r) {
+        return modifyRoute(p.getInterfaceName(), p, r, 0, true);
+    }
+
+    private boolean removeRoute(LinkProperties p, RouteInfo r) {
+        return modifyRoute(p.getInterfaceName(), p, r, 0, false);
+    }
+
+    private boolean addRouteToAddress(LinkProperties lp, InetAddress addr) {
+        return modifyRouteToAddress(lp, addr, true);
+    }
+
+    private boolean removeRouteToAddress(LinkProperties lp, InetAddress addr) {
+        return modifyRouteToAddress(lp, addr, false);
+    }
+
+    private boolean modifyRouteToAddress(LinkProperties lp, InetAddress addr, boolean doAdd) {
+        RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), addr);
+        if (bestRoute == null) {
+            bestRoute = RouteInfo.makeHostRoute(addr);
+        } else {
+            if (bestRoute.getGateway().equals(addr)) {
+                // if there is no better route, add the implied hostroute for our gateway
+                bestRoute = RouteInfo.makeHostRoute(addr);
+            } else {
+                // if we will connect to this through another route, add a direct route
+                // to it's gateway
+                bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway());
+            }
+        }
+        return modifyRoute(lp.getInterfaceName(), lp, bestRoute, 0, doAdd);
+    }
+
+    private boolean modifyRoute(String ifaceName, LinkProperties lp, RouteInfo r, int cycleCount,
+            boolean doAdd) {
+        if ((ifaceName == null) || (lp == null) || (r == null)) return false;
+
+        if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) {
+            loge("Error adding route - too much recursion");
             return false;
         }
+
+        if (r.isHostRoute() == false) {
+            RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), r.getGateway());
+            if (bestRoute != null) {
+                if (bestRoute.getGateway().equals(r.getGateway())) {
+                    // if there is no better route, add the implied hostroute for our gateway
+                    bestRoute = RouteInfo.makeHostRoute(r.getGateway());
+                } else {
+                    // if we will connect to our gateway through another route, add a direct
+                    // route to it's gateway
+                    bestRoute = RouteInfo.makeHostRoute(r.getGateway(), bestRoute.getGateway());
+                }
+                modifyRoute(ifaceName, lp, bestRoute, cycleCount+1, doAdd);
+            }
+        }
+        if (doAdd) {
+            if (DBG) log("Adding " + r + " for interface " + ifaceName);
+            mAddedRoutes.add(r);
+            try {
+                mNetd.addRoute(ifaceName, r);
+            } catch (Exception e) {
+                // never crash - catch them all
+                loge("Exception trying to add a route: " + e);
+                return false;
+            }
+        } else {
+            // if we remove this one and there are no more like it, then refcount==0 and
+            // we can remove it from the table
+            mAddedRoutes.remove(r);
+            if (mAddedRoutes.contains(r) == false) {
+                if (DBG) log("Removing " + r + " for interface " + ifaceName);
+                try {
+                    mNetd.removeRoute(ifaceName, r);
+                } catch (Exception e) {
+                    // never crash - catch them all
+                    loge("Exception trying to remove a route: " + e);
+                    return false;
+                }
+            } else {
+                if (DBG) log("not removing " + r + " as it's still in use");
+            }
+        }
+        return true;
     }
 
     /**
      * @see ConnectivityManager#getBackgroundDataSetting()
      */
     public boolean getBackgroundDataSetting() {
-        return Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.BACKGROUND_DATA, 1) == 1;
+        return mBackgroundDataEnabled.get();
     }
 
     /**
@@ -977,88 +1069,70 @@
                 android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
                 "ConnectivityService");
 
+        mBackgroundDataEnabled.set(allowBackgroundDataUsage);
+
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_BACKGROUND_DATA,
                 (allowBackgroundDataUsage ? ENABLED : DISABLED), 0));
     }
 
     private void handleSetBackgroundData(boolean enabled) {
-        if (enabled != getBackgroundDataSetting()) {
-            Settings.Secure.putInt(mContext.getContentResolver(),
-                    Settings.Secure.BACKGROUND_DATA, enabled ? 1 : 0);
-            Intent broadcast = new Intent(
-                    ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
-            mContext.sendBroadcast(broadcast);
-        }
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.BACKGROUND_DATA, enabled ? 1 : 0);
+        Intent broadcast = new Intent(
+                ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
+        mContext.sendBroadcast(broadcast);
     }
 
     /**
      * @see ConnectivityManager#getMobileDataEnabled()
      */
     public boolean getMobileDataEnabled() {
+        // TODO: This detail should probably be in DataConnectionTracker's
+        //       which is where we store the value and maybe make this
+        //       asynchronous.
         enforceAccessPermission();
         boolean retVal = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.MOBILE_DATA, 1) == 1;
-        if (DBG) Slog.d(TAG, "getMobileDataEnabled returning " + retVal);
+        if (DBG) log("getMobileDataEnabled returning " + retVal);
         return retVal;
     }
 
+    public void setDataDependency(int networkType, boolean met) {
+        enforceConnectivityInternalPermission();
+
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_DEPENDENCY_MET,
+                (met ? ENABLED : DISABLED), networkType));
+    }
+
+    private void handleSetDependencyMet(int networkType, boolean met) {
+        if (mNetTrackers[networkType] != null) {
+            if (DBG) {
+                log("handleSetDependencyMet(" + networkType + ", " + met + ")");
+            }
+            mNetTrackers[networkType].setDependencyMet(met);
+        }
+    }
+
     /**
      * @see ConnectivityManager#setMobileDataEnabled(boolean)
      */
     public void setMobileDataEnabled(boolean enabled) {
         enforceChangePermission();
-        if (DBG) Slog.d(TAG, "setMobileDataEnabled(" + enabled + ")");
+        if (DBG) log("setMobileDataEnabled(" + enabled + ")");
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA,
-            (enabled ? ENABLED : DISABLED), 0));
+                (enabled ? ENABLED : DISABLED), 0));
     }
 
     private void handleSetMobileData(boolean enabled) {
-        if (getMobileDataEnabled() == enabled) return;
-
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.MOBILE_DATA, enabled ? 1 : 0);
-
-        if (enabled) {
-            if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
-                if (DBG) {
-                    Slog.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]);
-                }
-                mNetTrackers[ConnectivityManager.TYPE_MOBILE].reconnect();
+        if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
+            if (DBG) {
+                Slog.d(TAG, mNetTrackers[ConnectivityManager.TYPE_MOBILE].toString() + enabled);
             }
-            if (mNetTrackers[ConnectivityManager.TYPE_WIMAX] != null) {
-                if (DBG) {
-                    Slog.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_WIMAX]);
-                }
-                mNetTrackers[ConnectivityManager.TYPE_WIMAX].reconnect();
-            }
-        } else {
-            for (NetworkStateTracker nt : mNetTrackers) {
-                if (nt == null) continue;
-                int netType = nt.getNetworkInfo().getType();
-                if (mNetAttributes[netType].mRadio == ConnectivityManager.TYPE_MOBILE) {
-                    if (DBG) Slog.d(TAG, "tearing down " + nt);
-                    nt.teardown();
-                }
-            }
-            if (mNetTrackers[ConnectivityManager.TYPE_WIMAX] != null) {
-                mNetTrackers[ConnectivityManager.TYPE_WIMAX].teardown();
-            }
+            mNetTrackers[ConnectivityManager.TYPE_MOBILE].setDataEnable(enabled);
         }
     }
 
-    private int getNumConnectedNetworks() {
-        int numConnectedNets = 0;
-
-        for (NetworkStateTracker nt : mNetTrackers) {
-            if (nt != null && nt.getNetworkInfo().isConnected() &&
-                    !nt.isTeardownRequested()) {
-                ++numConnectedNets;
-            }
-        }
-        return numConnectedNets;
-    }
-
     private void enforceAccessPermission() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.ACCESS_NETWORK_STATE,
@@ -1084,6 +1158,12 @@
                 "ConnectivityService");
     }
 
+    private void enforceConnectivityInternalPermission() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CONNECTIVITY_INTERNAL,
+                "ConnectivityService");
+    }
+
     /**
      * Handle a {@code DISCONNECTED} event. If this pertains to the non-active
      * network, we ignore it. If it is for the active network, we send out a
@@ -1102,7 +1182,7 @@
          * getting the disconnect for a network that we explicitly disabled
          * in accordance with network preference policies.
          */
-        if (!mNetAttributes[prevNetType].isDefault()) {
+        if (!mNetConfigs[prevNetType].isDefault()) {
             List pids = mNetRequestersPids[prevNetType];
             for (int i = 0; i<pids.size(); i++) {
                 Integer pid = (Integer)pids.get(i);
@@ -1127,7 +1207,7 @@
                     info.getExtraInfo());
         }
 
-        if (mNetAttributes[prevNetType].isDefault()) {
+        if (mNetConfigs[prevNetType].isDefault()) {
             tryFailover(prevNetType);
             if (mActiveDefaultNetwork != -1) {
                 NetworkInfo switchTo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
@@ -1138,8 +1218,30 @@
             }
         }
         intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished);
+
+        // Reset interface if no other connections are using the same interface
+        boolean doReset = true;
+        LinkProperties linkProperties = mNetTrackers[prevNetType].getLinkProperties();
+        if (linkProperties != null) {
+            String oldIface = linkProperties.getInterfaceName();
+            if (TextUtils.isEmpty(oldIface) == false) {
+                for (NetworkStateTracker networkStateTracker : mNetTrackers) {
+                    if (networkStateTracker == null) continue;
+                    NetworkInfo networkInfo = networkStateTracker.getNetworkInfo();
+                    if (networkInfo.isConnected() && networkInfo.getType() != prevNetType) {
+                        LinkProperties l = networkStateTracker.getLinkProperties();
+                        if (l == null) continue;
+                        if (oldIface.equals(l.getInterfaceName())) {
+                            doReset = false;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
         // do this before we broadcast the change
-        handleConnectivityChange(prevNetType);
+        handleConnectivityChange(prevNetType, doReset);
 
         sendStickyBroadcast(intent);
         /*
@@ -1153,38 +1255,46 @@
 
     private void tryFailover(int prevNetType) {
         /*
-         * If this is a default network, check if other defaults are available
-         * or active
+         * If this is a default network, check if other defaults are available.
+         * Try to reconnect on all available and let them hash it out when
+         * more than one connects.
          */
-        if (mNetAttributes[prevNetType].isDefault()) {
+        if (mNetConfigs[prevNetType].isDefault()) {
             if (mActiveDefaultNetwork == prevNetType) {
                 mActiveDefaultNetwork = -1;
             }
 
-            boolean noMobileData = !getMobileDataEnabled();
+            // don't signal a reconnect for anything lower or equal priority than our
+            // current connected default
+            // TODO - don't filter by priority now - nice optimization but risky
+//            int currentPriority = -1;
+//            if (mActiveDefaultNetwork != -1) {
+//                currentPriority = mNetConfigs[mActiveDefaultNetwork].mPriority;
+//            }
             for (int checkType=0; checkType <= ConnectivityManager.MAX_NETWORK_TYPE; checkType++) {
                 if (checkType == prevNetType) continue;
-                if (mNetAttributes[checkType] == null) continue;
-                if (mNetAttributes[checkType].isDefault() == false) continue;
-                if (mNetAttributes[checkType].mRadio == ConnectivityManager.TYPE_MOBILE &&
-                        noMobileData) {
-                    Slog.e(TAG, "not failing over to mobile type " + checkType +
-                            " because Mobile Data Disabled");
-                    continue;
-                }
-                if (mNetAttributes[checkType].mRadio == ConnectivityManager.TYPE_WIMAX &&
-                        noMobileData) {
-                    Slog.e(TAG, "not failing over to mobile type " + checkType +
-                            " because Mobile Data Disabled");
-                    continue;
-                }
+                if (mNetConfigs[checkType] == null) continue;
+                if (!mNetConfigs[checkType].isDefault()) continue;
+
+// Enabling the isAvailable() optimization caused mobile to not get
+// selected if it was in the middle of error handling. Specifically
+// a moble connection that took 30 seconds to complete the DEACTIVATE_DATA_CALL
+// would not be available and we wouldn't get connected to anything.
+// So removing the isAvailable() optimization below for now. TODO: This
+// optimization should work and we need to investigate why it doesn't work.
+// This could be related to how DEACTIVATE_DATA_CALL is reporting its
+// complete before it is really complete.
+//                if (!mNetTrackers[checkType].isAvailable()) continue;
+
+//                if (currentPriority >= mNetConfigs[checkType].mPriority) continue;
+
                 NetworkStateTracker checkTracker = mNetTrackers[checkType];
                 NetworkInfo checkInfo = checkTracker.getNetworkInfo();
                 if (!checkInfo.isConnectedOrConnecting() || checkTracker.isTeardownRequested()) {
                     checkInfo.setFailover(true);
                     checkTracker.reconnect();
                 }
-                if (DBG) Slog.d(TAG, "Attempting to switch to " + checkInfo.getTypeName());
+                if (DBG) log("Attempting to switch to " + checkInfo.getTypeName());
             }
         }
     }
@@ -1231,7 +1341,7 @@
         } else {
             reasonText = " (" + reason + ").";
         }
-        Slog.e(TAG, "Attempt to connect to " + info.getTypeName() + " failed" + reasonText);
+        loge("Attempt to connect to " + info.getTypeName() + " failed" + reasonText);
 
         Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
         intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
@@ -1249,7 +1359,7 @@
             info.setFailover(false);
         }
 
-        if (mNetAttributes[info.getType()].isDefault()) {
+        if (mNetConfigs[info.getType()].isDefault()) {
             tryFailover(info.getType());
             if (mActiveDefaultNetwork != -1) {
                 NetworkInfo switchTo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
@@ -1292,6 +1402,8 @@
                 mInitialBroadcast = null;
             }
         }
+        // load the global proxy at startup
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_APPLY_GLOBAL_HTTP_PROXY));
     }
 
     private void handleConnect(NetworkInfo info) {
@@ -1303,32 +1415,43 @@
 
         // if this is a default net and other default is running
         // kill the one not preferred
-        if (mNetAttributes[type].isDefault()) {
+        if (mNetConfigs[type].isDefault()) {
             if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
                 if ((type != mNetworkPreference &&
-                        mNetAttributes[mActiveDefaultNetwork].mPriority >
-                        mNetAttributes[type].mPriority) ||
+                        mNetConfigs[mActiveDefaultNetwork].priority >
+                        mNetConfigs[type].priority) ||
                         mNetworkPreference == mActiveDefaultNetwork) {
                         // don't accept this one
-                        if (DBG) Slog.v(TAG, "Not broadcasting CONNECT_ACTION " +
+                        if (DBG) {
+                            log("Not broadcasting CONNECT_ACTION " +
                                 "to torn down network " + info.getTypeName());
+                        }
                         teardown(thisNet);
                         return;
                 } else {
                     // tear down the other
                     NetworkStateTracker otherNet =
                             mNetTrackers[mActiveDefaultNetwork];
-                    if (DBG) Slog.v(TAG, "Policy requires " +
-                            otherNet.getNetworkInfo().getTypeName() +
+                    if (DBG) {
+                        log("Policy requires " + otherNet.getNetworkInfo().getTypeName() +
                             " teardown");
+                    }
                     if (!teardown(otherNet)) {
-                        Slog.e(TAG, "Network declined teardown request");
+                        loge("Network declined teardown request");
                         teardown(thisNet);
                         return;
                     }
-                    if (isFailover) {
-                        otherNet.releaseWakeLock();
-                    }
+                }
+            }
+            synchronized (ConnectivityService.this) {
+                // have a new default network, release the transition wakelock in a second
+                // if it's held.  The second pause is to allow apps to reconnect over the
+                // new network
+                if (mNetTransitionWakeLock.isHeld()) {
+                    mHandler.sendMessageDelayed(mHandler.obtainMessage(
+                            EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
+                            mNetTransitionWakeLockSerialNumber, 0),
+                            1000);
                 }
             }
             mActiveDefaultNetwork = type;
@@ -1342,91 +1465,258 @@
             //reportNetworkCondition(mActiveDefaultNetwork, 100);
         }
         thisNet.setTeardownRequested(false);
-        thisNet.updateNetworkSettings();
-        handleConnectivityChange(type);
+        updateNetworkSettings(thisNet);
+        handleConnectivityChange(type, false);
         sendConnectedBroadcast(info);
     }
 
-    private void handleScanResultsAvailable(NetworkInfo info) {
-        int networkType = info.getType();
-        if (networkType != ConnectivityManager.TYPE_WIFI) {
-            if (DBG) Slog.v(TAG, "Got ScanResultsAvailable for " +
-                    info.getTypeName() + " network. Don't know how to handle.");
-        }
-
-        mNetTrackers[networkType].interpretScanResultsAvailable();
-    }
-
-    private void handleNotificationChange(boolean visible, int id,
-            Notification notification) {
-        NotificationManager notificationManager = (NotificationManager) mContext
-                .getSystemService(Context.NOTIFICATION_SERVICE);
-
-        if (visible) {
-            notificationManager.notify(id, notification);
-        } else {
-            notificationManager.cancel(id);
-        }
-    }
-
     /**
-     * After a change in the connectivity state of any network, We're mainly
-     * concerned with making sure that the list of DNS servers is setupup
+     * After a change in the connectivity state of a network. We're mainly
+     * concerned with making sure that the list of DNS servers is set up
      * according to which networks are connected, and ensuring that the
      * right routing table entries exist.
      */
-    private void handleConnectivityChange(int netType) {
+    private void handleConnectivityChange(int netType, boolean doReset) {
+        int resetMask = doReset ? NetworkUtils.RESET_ALL_ADDRESSES : 0;
+
         /*
          * If a non-default network is enabled, add the host routes that
          * will allow it's DNS servers to be accessed.
          */
         handleDnsConfigurationChange(netType);
 
+        LinkProperties curLp = mCurrentLinkProperties[netType];
+        LinkProperties newLp = null;
+
         if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
-            if (mNetAttributes[netType].isDefault()) {
-                mNetTrackers[netType].addDefaultRoute();
-            } else {
-                // many radios add a default route even when we don't want one.
-                // remove the default interface unless we need it for our active network
-                if (mActiveDefaultNetwork != -1) {
-                    String defaultIface = mNetTrackers[mActiveDefaultNetwork].getInterfaceName();
-                    if (defaultIface != null &&
-                            !defaultIface.equals(mNetTrackers[netType].getInterfaceName())) {
-                        mNetTrackers[netType].removeDefaultRoute();
+            newLp = mNetTrackers[netType].getLinkProperties();
+            if (VDBG) {
+                log("handleConnectivityChange: changed linkProperty[" + netType + "]:" +
+                        " doReset=" + doReset + " resetMask=" + resetMask +
+                        "\n   curLp=" + curLp +
+                        "\n   newLp=" + newLp);
+            }
+
+            if (curLp != null) {
+                if (curLp.isIdenticalInterfaceName(newLp)) {
+                    CompareResult<LinkAddress> car = curLp.compareAddresses(newLp);
+                    if ((car.removed.size() != 0) || (car.added.size() != 0)) {
+                        for (LinkAddress linkAddr : car.removed) {
+                            if (linkAddr.getAddress() instanceof Inet4Address) {
+                                resetMask |= NetworkUtils.RESET_IPV4_ADDRESSES;
+                            }
+                            if (linkAddr.getAddress() instanceof Inet6Address) {
+                                resetMask |= NetworkUtils.RESET_IPV6_ADDRESSES;
+                            }
+                        }
+                        if (DBG) {
+                            log("handleConnectivityChange: addresses changed" +
+                                    " linkProperty[" + netType + "]:" + " resetMask=" + resetMask +
+                                    "\n   car=" + car);
+                        }
+                    } else {
+                        if (DBG) {
+                            log("handleConnectivityChange: address are the same reset per doReset" +
+                                   " linkProperty[" + netType + "]:" +
+                                   " resetMask=" + resetMask);
+                        }
                     }
+                } else {
+                    resetMask = NetworkUtils.RESET_ALL_ADDRESSES;
+                    log("handleConnectivityChange: interface not not equivalent reset both" +
+                            " linkProperty[" + netType + "]:" +
+                            " resetMask=" + resetMask);
                 }
-                mNetTrackers[netType].addPrivateDnsRoutes();
+            }
+            if (mNetConfigs[netType].isDefault()) {
+                handleApplyDefaultProxy(netType);
             }
         } else {
-            if (mNetAttributes[netType].isDefault()) {
-                mNetTrackers[netType].removeDefaultRoute();
-            } else {
-                mNetTrackers[netType].removePrivateDnsRoutes();
+            if (VDBG) {
+                log("handleConnectivityChange: changed linkProperty[" + netType + "]:" +
+                        " doReset=" + doReset + " resetMask=" + resetMask +
+                        "\n  curLp=" + curLp +
+                        "\n  newLp= null");
+            }
+        }
+        mCurrentLinkProperties[netType] = newLp;
+        updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault());
+
+        if (doReset || resetMask != 0) {
+            LinkProperties linkProperties = mNetTrackers[netType].getLinkProperties();
+            if (linkProperties != null) {
+                String iface = linkProperties.getInterfaceName();
+                if (TextUtils.isEmpty(iface) == false) {
+                    if (DBG) log("resetConnections(" + iface + ", " + resetMask + ")");
+                    NetworkUtils.resetConnections(iface, resetMask);
+                }
+            }
+        }
+
+        // TODO: Temporary notifying upstread change to Tethering.
+        //       @see bug/4455071
+        /** Notify TetheringService if interface name has been changed. */
+        if (TextUtils.equals(mNetTrackers[netType].getNetworkInfo().getReason(),
+                             Phone.REASON_LINK_PROPERTIES_CHANGED)) {
+            if (isTetheringSupported()) {
+                mTethering.handleTetherIfaceChange();
             }
         }
     }
 
     /**
+     * Add and remove routes using the old properties (null if not previously connected),
+     * new properties (null if becoming disconnected).  May even be double null, which
+     * is a noop.
+     * Uses isLinkDefault to determine if default routes should be set or conversely if
+     * host routes should be set to the dns servers
+     */
+    private void updateRoutes(LinkProperties newLp, LinkProperties curLp, boolean isLinkDefault) {
+        Collection<RouteInfo> routesToAdd = null;
+        CompareResult<InetAddress> dnsDiff = new CompareResult<InetAddress>();
+        CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>();
+        if (curLp != null) {
+            // check for the delta between the current set and the new
+            routeDiff = curLp.compareRoutes(newLp);
+            dnsDiff = curLp.compareDnses(newLp);
+        } else if (newLp != null) {
+            routeDiff.added = newLp.getRoutes();
+            dnsDiff.added = newLp.getDnses();
+        }
+
+        for (RouteInfo r : routeDiff.removed) {
+            if (isLinkDefault || ! r.isDefaultRoute()) {
+                removeRoute(curLp, r);
+            }
+        }
+
+        for (RouteInfo r :  routeDiff.added) {
+            if (isLinkDefault || ! r.isDefaultRoute()) {
+                addRoute(newLp, r);
+            }
+        }
+
+        if (!isLinkDefault) {
+            // handle DNS routes
+            if (routeDiff.removed.size() == 0 && routeDiff.added.size() == 0) {
+                // no change in routes, check for change in dns themselves
+                for (InetAddress oldDns : dnsDiff.removed) {
+                    removeRouteToAddress(curLp, oldDns);
+                }
+                for (InetAddress newDns : dnsDiff.added) {
+                    addRouteToAddress(newLp, newDns);
+                }
+            } else {
+                // routes changed - remove all old dns entries and add new
+                if (curLp != null) {
+                    for (InetAddress oldDns : curLp.getDnses()) {
+                        removeRouteToAddress(curLp, oldDns);
+                    }
+                }
+                if (newLp != null) {
+                    for (InetAddress newDns : newLp.getDnses()) {
+                        addRouteToAddress(newLp, newDns);
+                    }
+                }
+            }
+        }
+    }
+
+
+   /**
+     * Reads the network specific TCP buffer sizes from SystemProperties
+     * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system
+     * wide use
+     */
+   public void updateNetworkSettings(NetworkStateTracker nt) {
+        String key = nt.getTcpBufferSizesPropName();
+        String bufferSizes = SystemProperties.get(key);
+
+        if (bufferSizes.length() == 0) {
+            loge(key + " not found in system properties. Using defaults");
+
+            // Setting to default values so we won't be stuck to previous values
+            key = "net.tcp.buffersize.default";
+            bufferSizes = SystemProperties.get(key);
+        }
+
+        // Set values in kernel
+        if (bufferSizes.length() != 0) {
+            if (DBG) {
+                log("Setting TCP values: [" + bufferSizes
+                        + "] which comes from [" + key + "]");
+            }
+            setBufferSize(bufferSizes);
+        }
+    }
+
+   /**
+     * Writes TCP buffer sizes to /sys/kernel/ipv4/tcp_[r/w]mem_[min/def/max]
+     * which maps to /proc/sys/net/ipv4/tcp_rmem and tcpwmem
+     *
+     * @param bufferSizes in the format of "readMin, readInitial, readMax,
+     *        writeMin, writeInitial, writeMax"
+     */
+    private void setBufferSize(String bufferSizes) {
+        try {
+            String[] values = bufferSizes.split(",");
+
+            if (values.length == 6) {
+              final String prefix = "/sys/kernel/ipv4/tcp_";
+                stringToFile(prefix + "rmem_min", values[0]);
+                stringToFile(prefix + "rmem_def", values[1]);
+                stringToFile(prefix + "rmem_max", values[2]);
+                stringToFile(prefix + "wmem_min", values[3]);
+                stringToFile(prefix + "wmem_def", values[4]);
+                stringToFile(prefix + "wmem_max", values[5]);
+            } else {
+                loge("Invalid buffersize string: " + bufferSizes);
+            }
+        } catch (IOException e) {
+            loge("Can't set tcp buffer sizes:" + e);
+        }
+    }
+
+   /**
+     * Writes string to file. Basically same as "echo -n $string > $filename"
+     *
+     * @param filename
+     * @param string
+     * @throws IOException
+     */
+    private void stringToFile(String filename, String string) throws IOException {
+        FileWriter out = new FileWriter(filename);
+        try {
+            out.write(string);
+        } finally {
+            out.close();
+        }
+    }
+
+
+    /**
      * Adjust the per-process dns entries (net.dns<x>.<pid>) based
      * on the highest priority active net which this process requested.
      * If there aren't any, clear it out
      */
     private void reassessPidDns(int myPid, boolean doBump)
     {
-        if (DBG) Slog.d(TAG, "reassessPidDns for pid " + myPid);
+        if (DBG) log("reassessPidDns for pid " + myPid);
         for(int i : mPriorityList) {
-            if (mNetAttributes[i].isDefault()) {
+            if (mNetConfigs[i].isDefault()) {
                 continue;
             }
             NetworkStateTracker nt = mNetTrackers[i];
             if (nt.getNetworkInfo().isConnected() &&
                     !nt.isTeardownRequested()) {
+                LinkProperties p = nt.getLinkProperties();
+                if (p == null) continue;
                 List pids = mNetRequestersPids[i];
                 for (int j=0; j<pids.size(); j++) {
                     Integer pid = (Integer)pids.get(j);
                     if (pid.intValue() == myPid) {
-                        String[] dnsList = nt.getNameServers();
-                        writePidDns(dnsList, myPid);
+                        Collection<InetAddress> dnses = p.getDnses();
+                        writePidDns(dnses, myPid);
                         if (doBump) {
                             bumpDns();
                         }
@@ -1448,13 +1738,18 @@
         }
     }
 
-    private void writePidDns(String[] dnsList, int pid) {
+    // return true if results in a change
+    private boolean writePidDns(Collection <InetAddress> dnses, int pid) {
         int j = 1;
-        for (String dns : dnsList) {
-            if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
-                SystemProperties.set("net.dns" + j++ + "." + pid, dns);
+        boolean changed = false;
+        for (InetAddress dns : dnses) {
+            String dnsString = dns.getHostAddress();
+            if (changed || !dnsString.equals(SystemProperties.get("net.dns" + j + "." + pid))) {
+                changed = true;
+                SystemProperties.set("net.dns" + j++ + "." + pid, dns.getHostAddress());
             }
         }
+        return changed;
     }
 
     private void bumpDns() {
@@ -1470,27 +1765,57 @@
             } catch (NumberFormatException e) {}
         }
         SystemProperties.set("net.dnschange", "" + (n+1));
+        /*
+         * Tell the VMs to toss their DNS caches
+         */
+        Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
+        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        /*
+         * Connectivity events can happen before boot has completed ...
+         */
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        mContext.sendBroadcast(intent);
     }
 
     private void handleDnsConfigurationChange(int netType) {
         // add default net's dns entries
         NetworkStateTracker nt = mNetTrackers[netType];
         if (nt != null && nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
-            String[] dnsList = nt.getNameServers();
+            LinkProperties p = nt.getLinkProperties();
+            if (p == null) return;
+            Collection<InetAddress> dnses = p.getDnses();
             try {
-                mNetd.setDnsServersForInterface(Integer.toString(netType), dnsList);
+                mNetd.setDnsServersForInterface(Integer.toString(netType),
+                        NetworkUtils.makeStrings(dnses));
             } catch (Exception e) {
                 Slog.e(TAG, "exception setting dns servers: " + e);
             }
-            if (mNetAttributes[netType].isDefault()) {
+            boolean changed = false;
+            if (mNetConfigs[netType].isDefault()) {
                 int j = 1;
-                for (String dns : dnsList) {
-                    if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
+                if (dnses.size() == 0 && mDefaultDns != null) {
+                    String dnsString = mDefaultDns.getHostAddress();
+                    if (!dnsString.equals(SystemProperties.get("net.dns1"))) {
                         if (DBG) {
-                            Slog.d(TAG, "adding dns " + dns + " for " +
+                            log("no dns provided - using " + dnsString);
+                        }
+                        changed = true;
+                        SystemProperties.set("net.dns1", dnsString);
+                    }
+                    j++;
+                } else {
+                    for (InetAddress dns : dnses) {
+                        String dnsString = dns.getHostAddress();
+                        if (!changed && dnsString.equals(SystemProperties.get("net.dns" + j))) {
+                            j++;
+                            continue;
+                        }
+                        if (DBG) {
+                            log("adding dns " + dns + " for " +
                                     nt.getNetworkInfo().getTypeName());
                         }
-                        SystemProperties.set("net.dns" + j++, dns);
+                        changed = true;
+                        SystemProperties.set("net.dns" + j++, dnsString);
                     }
                 }
                 try {
@@ -1498,8 +1823,11 @@
                 } catch (Exception e) {
                     Slog.e(TAG, "exception setting default dns interface: " + e);}
                 for (int k=j ; k<mNumDnsEntries; k++) {
-                    if (DBG) Slog.d(TAG, "erasing net.dns" + k);
-                    SystemProperties.set("net.dns" + k, "");
+                    if (changed || !TextUtils.isEmpty(SystemProperties.get("net.dns" + k))) {
+                        if (DBG) log("erasing net.dns" + k);
+                        changed = true;
+                        SystemProperties.set("net.dns" + k, "");
+                    }
                 }
                 mNumDnsEntries = j;
             } else {
@@ -1507,14 +1835,14 @@
                 List pids = mNetRequestersPids[netType];
                 for (int y=0; y< pids.size(); y++) {
                     Integer pid = (Integer)pids.get(y);
-                    writePidDns(dnsList, pid.intValue());
+                    changed = writePidDns(dnses, pid.intValue());
                 }
             }
+            if (changed) bumpDns();
         }
-        bumpDns();
     }
 
-    private int getRestoreDefaultNetworkDelay() {
+    private int getRestoreDefaultNetworkDelay(int networkType) {
         String restoreDefaultNetworkDelayStr = SystemProperties.get(
                 NETWORK_RESTORE_DELAY_PROP_NAME);
         if(restoreDefaultNetworkDelayStr != null &&
@@ -1524,7 +1852,14 @@
             } catch (NumberFormatException e) {
             }
         }
-        return RESTORE_DEFAULT_NETWORK_DELAY;
+        // if the system property isn't set, use the value for the apn type
+        int ret = RESTORE_DEFAULT_NETWORK_DELAY;
+
+        if ((networkType <= ConnectivityManager.MAX_NETWORK_TYPE) &&
+                (mNetConfigs[networkType] != null)) {
+            ret = mNetConfigs[networkType].restoreTime;
+        }
+        return ret;
     }
 
     @Override
@@ -1566,6 +1901,13 @@
         }
         pw.println();
 
+        synchronized (this) {
+            pw.println("NetworkTranstionWakeLock is currently " +
+                    (mNetTransitionWakeLock.isHeld() ? "" : "not ") + "held.");
+            pw.println("It was last requested for "+mNetTransitionWakeLockCausedBy);
+        }
+        pw.println();
+
         mTethering.dump(fd, pw, args);
 
         if (mInetLog != null) {
@@ -1579,6 +1921,10 @@
 
     // must be stateless - things change under us.
     private class MyHandler extends Handler {
+        public MyHandler(Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             NetworkInfo info;
@@ -1587,25 +1933,8 @@
                     info = (NetworkInfo) msg.obj;
                     int type = info.getType();
                     NetworkInfo.State state = info.getState();
-                    // only do this optimization for wifi.  It going into scan mode for location
-                    // services generates alot of noise.  Meanwhile the mms apn won't send out
-                    // subsequent notifications when on default cellular because it never
-                    // disconnects..  so only do this to wifi notifications.  Fixed better when the
-                    // APN notifications are standardized.
-                    if (mNetAttributes[type].mLastState == state &&
-                            mNetAttributes[type].mRadio == ConnectivityManager.TYPE_WIFI) {
-                        if (DBG) {
-                            // TODO - remove this after we validate the dropping doesn't break
-                            // anything
-                            Slog.d(TAG, "Dropping ConnectivityChange for " +
-                                    info.getTypeName() + ": " +
-                                    state + "/" + info.getDetailedState());
-                        }
-                        return;
-                    }
-                    mNetAttributes[type].mLastState = state;
 
-                    if (DBG) Slog.d(TAG, "ConnectivityChange for " +
+                    if (DBG) log("ConnectivityChange for " +
                             info.getTypeName() + ": " +
                             state + "/" + info.getDetailedState());
 
@@ -1640,29 +1969,25 @@
                         handleConnect(info);
                     }
                     break;
-
-                case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
-                    info = (NetworkInfo) msg.obj;
-                    handleScanResultsAvailable(info);
-                    break;
-
-                case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
-                    handleNotificationChange(msg.arg1 == 1, msg.arg2,
-                            (Notification) msg.obj);
-                    break;
-
                 case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
                     info = (NetworkInfo) msg.obj;
-                    type = info.getType();
-                    handleDnsConfigurationChange(type);
+                    // TODO: Temporary allowing network configuration
+                    //       change not resetting sockets.
+                    //       @see bug/4455071
+                    handleConnectivityChange(info.getType(), false);
                     break;
-
-                case NetworkStateTracker.EVENT_ROAMING_CHANGED:
-                    // fill me in
-                    break;
-
-                case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
-                    // fill me in
+                case EVENT_CLEAR_NET_TRANSITION_WAKELOCK:
+                    String causedBy = null;
+                    synchronized (ConnectivityService.this) {
+                        if (msg.arg1 == mNetTransitionWakeLockSerialNumber &&
+                                mNetTransitionWakeLock.isHeld()) {
+                            mNetTransitionWakeLock.release();
+                            causedBy = mNetTransitionWakeLockCausedBy;
+                        }
+                    }
+                    if (causedBy != null) {
+                        log("NetTransition Wakelock for " + causedBy + " released by timeout");
+                    }
                     break;
                 case EVENT_RESTORE_DEFAULT_NETWORK:
                     FeatureUser u = (FeatureUser)msg.obj;
@@ -1700,6 +2025,17 @@
                     handleSetMobileData(enabled);
                     break;
                 }
+                case EVENT_APPLY_GLOBAL_HTTP_PROXY:
+                {
+                    handleDeprecatedGlobalHttpProxy();
+                    break;
+                }
+                case EVENT_SET_DEPENDENCY_MET:
+                {
+                    boolean met = (msg.arg1 == ENABLED);
+                    handleSetDependencyMet(msg.arg2, met);
+                    break;
+                }
             }
         }
     }
@@ -1756,6 +2092,15 @@
         }
     }
 
+    public String[] getTetherableBluetoothRegexs() {
+        enforceTetherAccessPermission();
+        if (isTetheringSupported()) {
+            return mTethering.getTetherableBluetoothRegexs();
+        } else {
+            return new String[0];
+        }
+    }
+
     // TODO - move iface listing, queries, etc to new module
     // javadoc from interface
     public String[] getTetherableIfaces() {
@@ -1784,9 +2129,28 @@
         return tetherEnabledInSettings && mTetheringConfigValid;
     }
 
+    // An API NetworkStateTrackers can call when they lose their network.
+    // This will automatically be cleared after X seconds or a network becomes CONNECTED,
+    // whichever happens first.  The timer is started by the first caller and not
+    // restarted by subsequent callers.
+    public void requestNetworkTransitionWakelock(String forWhom) {
+        enforceConnectivityInternalPermission();
+        synchronized (this) {
+            if (mNetTransitionWakeLock.isHeld()) return;
+            mNetTransitionWakeLockSerialNumber++;
+            mNetTransitionWakeLock.acquire();
+            mNetTransitionWakeLockCausedBy = forWhom;
+        }
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(
+                EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
+                mNetTransitionWakeLockSerialNumber, 0),
+                mNetTransitionWakeLockTimeout);
+        return;
+    }
+
     // 100 percent is full good, 0 is full bad.
     public void reportInetCondition(int networkType, int percentage) {
-        if (DBG) Slog.d(TAG, "reportNetworkCondition(" + networkType + ", " + percentage + ")");
+        if (DBG) log("reportNetworkCondition(" + networkType + ", " + percentage + ")");
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.STATUS_BAR,
                 "ConnectivityService");
@@ -1808,22 +2172,22 @@
 
     private void handleInetConditionChange(int netType, int condition) {
         if (DBG) {
-            Slog.d(TAG, "Inet connectivity change, net=" +
+            log("Inet connectivity change, net=" +
                     netType + ", condition=" + condition +
                     ",mActiveDefaultNetwork=" + mActiveDefaultNetwork);
         }
         if (mActiveDefaultNetwork == -1) {
-            if (DBG) Slog.d(TAG, "no active default network - aborting");
+            if (DBG) log("no active default network - aborting");
             return;
         }
         if (mActiveDefaultNetwork != netType) {
-            if (DBG) Slog.d(TAG, "given net not default - aborting");
+            if (DBG) log("given net not default - aborting");
             return;
         }
         mDefaultInetCondition = condition;
         int delay;
         if (mInetConditionChangeInFlight == false) {
-            if (DBG) Slog.d(TAG, "starting a change hold");
+            if (DBG) log("starting a change hold");
             // setup a new hold to debounce this
             if (mDefaultInetCondition > 50) {
                 delay = Settings.Secure.getInt(mContext.getContentResolver(),
@@ -1838,37 +2202,190 @@
         } else {
             // we've set the new condition, when this hold ends that will get
             // picked up
-            if (DBG) Slog.d(TAG, "currently in hold - not setting new end evt");
+            if (DBG) log("currently in hold - not setting new end evt");
         }
     }
 
     private void handleInetConditionHoldEnd(int netType, int sequence) {
         if (DBG) {
-            Slog.d(TAG, "Inet hold end, net=" + netType +
+            log("Inet hold end, net=" + netType +
                     ", condition =" + mDefaultInetCondition +
                     ", published condition =" + mDefaultInetConditionPublished);
         }
         mInetConditionChangeInFlight = false;
 
         if (mActiveDefaultNetwork == -1) {
-            if (DBG) Slog.d(TAG, "no active default network - aborting");
+            if (DBG) log("no active default network - aborting");
             return;
         }
         if (mDefaultConnectionSequence != sequence) {
-            if (DBG) Slog.d(TAG, "event hold for obsolete network - aborting");
+            if (DBG) log("event hold for obsolete network - aborting");
             return;
         }
         if (mDefaultInetConditionPublished == mDefaultInetCondition) {
-            if (DBG) Slog.d(TAG, "no change in condition - aborting");
+            if (DBG) log("no change in condition - aborting");
             return;
         }
         NetworkInfo networkInfo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
         if (networkInfo.isConnected() == false) {
-            if (DBG) Slog.d(TAG, "default network not connected - aborting");
+            if (DBG) log("default network not connected - aborting");
             return;
         }
         mDefaultInetConditionPublished = mDefaultInetCondition;
         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);
+    }
+
+    private void loadGlobalProxy() {
+        ContentResolver res = mContext.getContentResolver();
+        String host = Settings.Secure.getString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST);
+        int port = Settings.Secure.getInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, 0);
+        String exclList = Settings.Secure.getString(res,
+                Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
+        if (!TextUtils.isEmpty(host)) {
+            ProxyProperties proxyProperties = new ProxyProperties(host, port, exclList);
+            synchronized (mGlobalProxyLock) {
+                mGlobalProxy = 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) log("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) {
+        if (proxy == null) proxy = new ProxyProperties("", 0, "");
+        log("sending Proxy Broadcast for " + proxy);
+        Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
+        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
+            Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
+        mContext.sendStickyBroadcast(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();
+        }
+    }
+
+    private void log(String s) {
+        Slog.d(TAG, s);
+    }
+
+    private void loge(String s) {
+        Slog.e(TAG, s);
+    }
+    int convertFeatureToNetworkType(String feature){
+        int networkType = -1;
+        if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
+            networkType = ConnectivityManager.TYPE_MOBILE_MMS;
+        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
+            networkType = ConnectivityManager.TYPE_MOBILE_SUPL;
+        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN) ||
+                TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN_ALWAYS)) {
+            networkType = ConnectivityManager.TYPE_MOBILE_DUN;
+        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
+            networkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
+        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_FOTA)) {
+            networkType = ConnectivityManager.TYPE_MOBILE_FOTA;
+        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_IMS)) {
+            networkType = ConnectivityManager.TYPE_MOBILE_IMS;
+        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_CBS)) {
+            networkType = ConnectivityManager.TYPE_MOBILE_CBS;
+        }
+        return networkType;
+    }
 }