Add net type to mobile for mobile-required traffic

This also refactors ConnectivityService a bit towards supporting multiple simultaneous connections by making each a seem like a seperate Network with it's own stateTracker, etc.
Also adds tracking of process death to clean orphaned startUsingNetworkFeature features.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 1429bc1..a127df0 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.os.Binder;
 import android.os.RemoteException;
 
 /**
@@ -114,15 +115,64 @@
     public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED =
             "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
 
-    public static final int TYPE_MOBILE = 0;
-    public static final int TYPE_WIFI   = 1;
+    /**
+     * The Default Mobile data connection.  When active, all data traffic
+     * will use this connection by default.  Should not coexist with other
+     * default connections.
+     */
+    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.
+     */
+    public static final int TYPE_WIFI        = 1;
+    /**
+     * An MMS-specific Mobile data connection.  This connection may be the
+     * same as {@link #TYPEMOBILE} but it may be different.  This is used
+     * by applications needing to talk to the carrier's Multimedia Messaging
+     * Service servers.  It may coexist with default data connections.
+     * {@hide}
+     */
+    public static final int TYPE_MOBILE_MMS  = 2;
+    /**
+     * A SUPL-specific Mobile data connection.  This connection may be the
+     * same as {@link #TYPEMOBILE} but it may be different.  This is used
+     * by applications needing to talk to the carrier's Secure User Plane
+     * Location servers for help locating the device.  It may coexist with
+     * default data connections.
+     * {@hide}
+     */
+    public static final int TYPE_MOBILE_SUPL = 3;
+    /**
+     * A DUN-specific Mobile data connection.  This connection may be the
+     * same as {@link #TYPEMOBILE} but it may be different.  This is used
+     * by applicaitons performing a Dial Up Networking bridge so that
+     * the carrier is aware of DUN traffic.  It may coexist with default data
+     * connections.
+     * {@hide}
+     */
+    public static final int TYPE_MOBILE_DUN  = 4;
+    /**
+     * A High Priority Mobile data connection.  This connection is typically
+     * the same as {@link #TYPEMOBILE} but the routing setup is different.
+     * Only requesting processes will have access to the Mobile DNS servers
+     * and only IP's explicitly requested via {@link #requestRouteToHost}
+     * will route over this interface.
+     *{@hide}
+     */
+    public static final int TYPE_MOBILE_HIPRI = 5;
+    /** {@hide} */
+    public static final int MAX_RADIO_TYPE   = TYPE_WIFI;
+    /** {@hide} */
+    public static final int MAX_NETWORK_TYPE = TYPE_MOBILE_HIPRI;
 
     public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI;
 
     private IConnectivityManager mService;
 
     static public boolean isNetworkTypeValid(int networkType) {
-        return networkType == TYPE_WIFI || networkType == TYPE_MOBILE;
+        return networkType >= 0 && networkType <= MAX_NETWORK_TYPE;
     }
 
     public void setNetworkPreference(int preference) {
@@ -195,7 +245,8 @@
      */
     public int startUsingNetworkFeature(int networkType, String feature) {
         try {
-            return mService.startUsingNetworkFeature(networkType, feature);
+            return mService.startUsingNetworkFeature(networkType, feature,
+                    new Binder());
         } catch (RemoteException e) {
             return -1;
         }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index de68598..9f59cce 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.net.NetworkInfo;
+import android.os.IBinder;
 
 /**
  * Interface that answers queries about, and allows changing, the
@@ -39,7 +40,8 @@
 
     boolean setRadio(int networkType, boolean turnOn);
 
-    int startUsingNetworkFeature(int networkType, in String feature);
+    int startUsingNetworkFeature(int networkType, in String feature,
+            in IBinder binder);
 
     int stopUsingNetworkFeature(int networkType, in String feature);
 
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 1064fb6..6b00900 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -32,9 +32,6 @@
 import android.util.Log;
 import android.text.TextUtils;
 
-import java.util.List;
-import java.util.ArrayList;
-
 /**
  * Track the state of mobile data connectivity. This is done by
  * receiving broadcast intents from the Phone process whenever
@@ -45,36 +42,47 @@
 public class MobileDataStateTracker extends NetworkStateTracker {
 
     private static final String TAG = "MobileDataStateTracker";
-    private static final boolean DBG = false;
+    private static final boolean DBG = true;
 
     private Phone.DataState mMobileDataState;
     private ITelephony mPhoneService;
-    private static final String[] sDnsPropNames = {
-          "net.rmnet0.dns1",
-          "net.rmnet0.dns2",
-          "net.eth0.dns1",
-          "net.eth0.dns2",
-          "net.eth0.dns3",
-          "net.eth0.dns4",
-          "net.gprs.dns1",
-          "net.gprs.dns2"
-    };
-    private List<String> mDnsServers;
-    private String mInterfaceName;
-    private int mDefaultGatewayAddr;
-    private int mLastCallingPid = -1;
+
+    private String mApnType;
+    private boolean mEnabled;
+    private boolean mTeardownRequested;
 
     /**
      * Create a new MobileDataStateTracker
      * @param context the application context of the caller
      * @param target a message handler for getting callbacks about state changes
+     * @param netType the ConnectivityManager network type
+     * @param apnType the Phone apnType
+     * @param tag the name of this network
      */
-    public MobileDataStateTracker(Context context, Handler target) {
-        super(context, target, ConnectivityManager.TYPE_MOBILE,
-              TelephonyManager.getDefault().getNetworkType(), "MOBILE",
-              TelephonyManager.getDefault().getNetworkTypeName());
+    public MobileDataStateTracker(Context context, Handler target,
+            int netType, String apnType, String tag) {
+        super(context, target, netType,
+                TelephonyManager.getDefault().getNetworkType(), tag,
+                TelephonyManager.getDefault().getNetworkTypeName());
+        mApnType = apnType;
         mPhoneService = null;
-        mDnsServers = new ArrayList<String>();
+        mTeardownRequested = false;
+        if(netType == ConnectivityManager.TYPE_MOBILE) {
+            mEnabled = true;
+        } else {
+            mEnabled = false;
+        }
+
+        mDnsPropNames = new String[] {
+                "net.rmnet0.dns1",
+                "net.rmnet0.dns2",
+                "net.eth0.dns1",
+                "net.eth0.dns2",
+                "net.eth0.dns3",
+                "net.eth0.dns4",
+                "net.gprs.dns1",
+                "net.gprs.dns2"};
+
     }
 
     /**
@@ -93,31 +101,70 @@
             mMobileDataState = Phone.DataState.DISCONNECTED;
     }
 
-    private static Phone.DataState getMobileDataState(Intent intent) {
+    private Phone.DataState getMobileDataState(Intent intent) {
         String str = intent.getStringExtra(Phone.STATE_KEY);
-        if (str != null)
-            return Enum.valueOf(Phone.DataState.class, str);
-        else
-            return Phone.DataState.DISCONNECTED;
+        if (str != null) {
+            String apnTypeList =
+                    intent.getStringExtra(Phone.DATA_APN_TYPES_KEY);
+            if (isApnTypeIncluded(apnTypeList)) {
+                return Enum.valueOf(Phone.DataState.class, str);
+            }
+        }
+        return Phone.DataState.DISCONNECTED;
+    }
+
+    private boolean isApnTypeIncluded(String typeList) {
+        /* comma seperated list - split and check */
+        if (typeList == null)
+            return false;
+
+        String[] list = typeList.split(",");
+        for(int i=0; i< list.length; i++) {
+            if (TextUtils.equals(list[i], mApnType) ||
+                TextUtils.equals(list[i], Phone.APN_TYPE_ALL)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     private class MobileDataStateReceiver extends BroadcastReceiver {
         public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
+            if (intent.getAction().equals(TelephonyIntents.
+                    ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
                 Phone.DataState state = getMobileDataState(intent);
-                String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY);
+                String reason =
+                        intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY);
                 String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
-                boolean unavailable = intent.getBooleanExtra(Phone.NETWORK_UNAVAILABLE_KEY, false);
-                if (DBG) Log.d(TAG, "Received " + intent.getAction() +
-                    " broadcast - state = " + state
-                    + ", unavailable = " + unavailable
-                    + ", reason = " + (reason == null ? "(unspecified)" : reason));
+
+                String apnTypeList =
+                        intent.getStringExtra(Phone.DATA_APN_TYPES_KEY);
+
+                boolean unavailable = intent.getBooleanExtra(
+                        Phone.NETWORK_UNAVAILABLE_KEY, false);
+                if (DBG) Log.d(TAG, mApnType + " Received "
+                        + intent.getAction() + " broadcast - state = "
+                        + state + ", unavailable = " + unavailable
+                        + ", reason = "
+                        + (reason == null ? "(unspecified)" : reason));
+
+                if ((!isApnTypeIncluded(apnTypeList)) || mEnabled == false) {
+                    if (DBG) Log.e(TAG, "  dropped - mEnabled = "+mEnabled);
+                    return;
+                }
+
+
                 mNetworkInfo.setIsAvailable(!unavailable);
                 if (mMobileDataState != state) {
                     mMobileDataState = state;
 
                     switch (state) {
                     case DISCONNECTED:
+                        if(mTeardownRequested) {
+                            mEnabled = false;
+                            mTeardownRequested = false;
+                        }
+
                         setDetailedState(DetailedState.DISCONNECTED, reason, apnName);
                         if (mInterfaceName != null) {
                             NetworkUtils.resetConnections(mInterfaceName);
@@ -136,12 +183,12 @@
                         if (mInterfaceName == null) {
                             Log.d(TAG, "CONNECTED event did not supply interface name.");
                         }
-                        setupDnsProperties();
                         setDetailedState(DetailedState.CONNECTED, reason, apnName);
                         break;
                     }
                 }
             } else if (intent.getAction().equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
+                mEnabled = false;
                 String reason = intent.getStringExtra(Phone.FAILURE_REASON_KEY);
                 String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
                 if (DBG) Log.d(TAG, "Received " + intent.getAction() + " broadcast" +
@@ -154,40 +201,6 @@
         }
     }
 
-    /**
-     * Make sure that route(s) exist to the carrier DNS server(s).
-     */
-    public void addPrivateRoutes() {
-        if (mInterfaceName != null) {
-            for (String addrString : mDnsServers) {
-                int addr = NetworkUtils.lookupHost(addrString);
-                if (addr != -1) {
-                    NetworkUtils.addHostRoute(mInterfaceName, addr);
-                }
-            }
-        }
-    }
-
-    public void removePrivateRoutes() {
-        if(mInterfaceName != null) {
-            NetworkUtils.removeHostRoutes(mInterfaceName);
-        }
-    }
-
-    public void removeDefaultRoute() {
-        if(mInterfaceName != null) {
-            mDefaultGatewayAddr = NetworkUtils.getDefaultRoute(mInterfaceName);
-            NetworkUtils.removeDefaultRoute(mInterfaceName);
-        }
-    }
-
-    public void restoreDefaultRoute() {
-        // 0 is not a valid address for a gateway
-        if (mInterfaceName != null && mDefaultGatewayAddr != 0) {
-            NetworkUtils.setDefaultRoute(mInterfaceName, mDefaultGatewayAddr);
-        }
-    }
-
     private void getPhoneService(boolean forceRefresh) {
         if ((mPhoneService == null) || forceRefresh) {
             mPhoneService = ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
@@ -219,15 +232,6 @@
     }
 
     /**
-     * Return the IP addresses of the DNS servers available for the mobile data
-     * network interface.
-     * @return a list of DNS addresses, with no holes.
-     */
-    public String[] getNameServers() {
-        return getNameServerList(sDnsPropNames);
-    }
-
-    /**
      * {@inheritDoc}
      * The mobile data network subtype indicates what generation network technology is in effect,
      * e.g., GPRS, EDGE, UMTS, etc.
@@ -273,54 +277,19 @@
      */
     @Override
     public boolean teardown() {
-        getPhoneService(false);
-        /*
-         * If the phone process has crashed in the past, we'll get a
-         * RemoteException and need to re-reference the service.
-         */
-        for (int retry = 0; retry < 2; retry++) {
-            if (mPhoneService == null) {
-                Log.w(TAG,
-                    "Ignoring mobile data teardown request because could not acquire PhoneService");
-                break;
-            }
-
-            try {
-                return mPhoneService.disableDataConnectivity();
-            } catch (RemoteException e) {
-                if (retry == 0) getPhoneService(true);
-            }
-        }
-
-        Log.w(TAG, "Failed to tear down mobile data connectivity");
-        return false;
+        mTeardownRequested = true;
+        return (setEnableApn(mApnType, false) != Phone.APN_REQUEST_FAILED);
     }
 
     /**
      * Re-enable mobile data connectivity after a {@link #teardown()}.
      */
     public boolean reconnect() {
-        getPhoneService(false);
-        /*
-         * If the phone process has crashed in the past, we'll get a
-         * RemoteException and need to re-reference the service.
-         */
-        for (int retry = 0; retry < 2; retry++) {
-            if (mPhoneService == null) {
-                Log.w(TAG,
-                    "Ignoring mobile data connect request because could not acquire PhoneService");
-                break;
-            }
-
-            try {
-                return mPhoneService.enableDataConnectivity();
-            } catch (RemoteException e) {
-                if (retry == 0) getPhoneService(true);
-            }
-        }
-
-        Log.w(TAG, "Failed to set up mobile data connectivity");
-        return false;
+        mEnabled = true;
+        mTeardownRequested = false;
+        mEnabled = (setEnableApn(mApnType, true) !=
+                Phone.APN_REQUEST_FAILED);
+        return mEnabled;
     }
 
     /**
@@ -374,14 +343,7 @@
      * </ul>
      */
     public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
-        if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
-            mLastCallingPid = callingPid;
-            return setEnableApn(Phone.APN_TYPE_MMS, true);
-        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
-            return setEnableApn(Phone.APN_TYPE_SUPL, true);
-        } else {
-            return -1;
-        }
+        return -1;
     }
 
     /**
@@ -397,13 +359,7 @@
      * the value {@code -1} always indicates failure.
      */
     public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
-        if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
-            return setEnableApn(Phone.APN_TYPE_MMS, false);
-        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
-            return setEnableApn(Phone.APN_TYPE_SUPL, false);
-        } else {
-            return -1;
-        }
+        return -1;
     }
 
     /**
@@ -433,43 +389,6 @@
         return sb.toString();
     }
 
-    private void setupDnsProperties() {
-        mDnsServers.clear();
-        // Set up per-process DNS server list on behalf of the MMS process
-        int i = 1;
-        if (mInterfaceName != null) {
-            for (String propName : sDnsPropNames) {
-                if (propName.indexOf(mInterfaceName) != -1) {
-                    String propVal = SystemProperties.get(propName);
-                    if (propVal != null && propVal.length() != 0 && !propVal.equals("0.0.0.0")) {
-                        mDnsServers.add(propVal);
-                        if (mLastCallingPid != -1) {
-                            SystemProperties.set("net.dns"  + i + "." + mLastCallingPid, propVal);
-                        }
-                        ++i;
-                    }
-                }
-            }
-        }
-        if (i == 1) {
-            Log.d(TAG, "DNS server addresses are not known.");
-        } else if (mLastCallingPid != -1) {
-            /*
-            * Bump the property that tells the name resolver library
-            * to reread the DNS server list from the properties.
-            */
-            String propVal = SystemProperties.get("net.dnschange");
-            if (propVal.length() != 0) {
-                try {
-                    int n = Integer.parseInt(propVal);
-                    SystemProperties.set("net.dnschange", "" + (n+1));
-                } catch (NumberFormatException e) {
-                }
-            }
-        }
-        mLastCallingPid = -1;
-    }
-
    /**
      * Internal method supporting the ENABLE_MMS feature.
      * @param apnType the type of APN to be enabled or disabled (e.g., mms)
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 37087ac..418f511 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -27,6 +27,7 @@
 import android.util.Config;
 import android.util.Log;
 
+
 /**
  * Each subclass of this class keeps track of the state of connectivity
  * of a network interface. All state information for a network should
@@ -40,11 +41,16 @@
     protected NetworkInfo mNetworkInfo;
     protected Context mContext;
     protected Handler mTarget;
+    protected String mInterfaceName;
+    protected String[] mDnsPropNames;
+    private boolean mPrivateDnsRouteSet;
+    protected int mDefaultGatewayAddr;
+    private boolean mDefaultRouteSet;
     private boolean mTeardownRequested;
 
-    private static boolean DBG = Config.LOGV; 
+    private static boolean DBG = Config.LOGV;
     private static final String TAG = "NetworkStateTracker";
-    
+
     public static final int EVENT_STATE_CHANGED = 1;
     public static final int EVENT_SCAN_RESULTS_AVAILABLE = 2;
     /**
@@ -56,6 +62,7 @@
     public static final int EVENT_CONFIGURATION_CHANGED = 4;
     public static final int EVENT_ROAMING_CHANGED = 5;
     public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 6;
+    public static final int EVENT_RESTORE_DEFAULT_NETWORK = 7;
 
     public NetworkStateTracker(Context context,
             Handler target,
@@ -67,6 +74,7 @@
         mContext = context;
         mTarget = target;
         mTeardownRequested = false;
+
         this.mNetworkInfo = new NetworkInfo(networkType, subType, typeName, subtypeName);
     }
 
@@ -75,19 +83,21 @@
     }
 
     /**
-     * Return the list of DNS servers associated with this network.
-     * @return a list of the IP addresses of the DNS servers available
-     * for the network.
-     */
-    public abstract String[] getNameServers();
-
-    /**
      * Return the system properties name associated with the tcp buffer sizes
      * for this network.
      */
     public abstract String getTcpBufferSizesPropName();
 
     /**
+     * Return the IP addresses of the DNS servers available for the mobile data
+     * network interface.
+     * @return a list of DNS addresses, with no holes.
+     */
+    public String[] getNameServers() {
+        return getNameServerList(mDnsPropNames);
+    }
+
+    /**
      * Return the IP addresses of the DNS servers available for this
      * network interface.
      * @param propertyNames the names of the system properties whose values
@@ -112,6 +122,50 @@
         return dnsAddresses;
     }
 
+    public void addPrivateDnsRoutes() {
+        if (DBG) Log.d(TAG, "addPrivateDnsRoutes for " + this +
+                "(" + mInterfaceName + ")");
+        if (mInterfaceName != null && !mPrivateDnsRouteSet) {
+            for (String addrString : getNameServers()) {
+                int addr = NetworkUtils.lookupHost(addrString);
+                if (addr != -1) {
+                    NetworkUtils.addHostRoute(mInterfaceName, addr);
+                }
+            }
+            mPrivateDnsRouteSet = true;
+        }
+    }
+
+    public void removePrivateDnsRoutes() {
+        if (DBG) Log.d(TAG, "removePrivateDnsRoutes for " + this +
+                "(" + mInterfaceName + ")");
+        // TODO - we should do this explicitly but the NetUtils api doesnt
+        // support this yet - must remove all.  No worse than before
+        if (mInterfaceName != null && mPrivateDnsRouteSet) {
+            NetworkUtils.removeHostRoutes(mInterfaceName);
+            mPrivateDnsRouteSet = false;
+        }
+    }
+
+    public void addDefaultRoute() {
+        if (DBG) Log.d(TAG, "addDefaultRoute for " + this + "(" +
+                mInterfaceName + "), GatewayAddr=" + mDefaultGatewayAddr);
+        if ((mInterfaceName != null) && (mDefaultGatewayAddr != 0) &&
+                mDefaultRouteSet == false) {
+            NetworkUtils.setDefaultRoute(mInterfaceName, mDefaultGatewayAddr);
+            mDefaultRouteSet = true;
+        }
+    }
+
+    public void removeDefaultRoute() {
+        if (DBG) Log.d(TAG, "removeDefaultRoute for " + this + "(" +
+                mInterfaceName + ")");
+        if (mInterfaceName != null && mDefaultRouteSet == true) {
+            NetworkUtils.removeDefaultRoute(mInterfaceName);
+            mDefaultRouteSet = false;
+        }
+    }
+
     /**
      * Reads the network specific TCP buffer sizes from SystemProperties
      * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system
@@ -209,6 +263,7 @@
      * @param extraInfo optional {@code String} providing extra information about the state change
      */
     public void setDetailedState(NetworkInfo.DetailedState state, String reason, String extraInfo) {
+        if (DBG) Log.d(TAG, "setDetailed state, old ="+mNetworkInfo.getDetailedState()+" and new state="+state);
         if (state != mNetworkInfo.getDetailedState()) {
             boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING);
             String lastReason = mNetworkInfo.getReason();
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f655b27..92c776c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -38,4 +38,23 @@
     <!-- Flag indicating whether Last Name comes before First Name.
         This becomes true in Japan, for example.-->
     <bool name="config_lastname_comes_before_firstname">false</bool>
+
+    <!-- This string array should be overridden by the device to present a list of network attributes.  This is used by the connectivity manager to decide which networks can coexist based on the hardward -->
+    <!-- An Array of "[type-name],[associated radio-name],[priority]  -->
+    <string-array translatable="false" name="networkAttributes">
+        <item>"default,wifi,0"</item>
+        <item>"default,mobile,0"</item>
+        <item>"mms,mobile,1"</item>
+        <item>"supl,mobile,1"</item>
+        <item>"dun,mobile,1"</item>
+        <item>"hipri,mobile,2"</item>
+    </string-array>
+
+    <!-- This string array should be overridden by the device to present a list of radio attributes.  This is used by the connectivity manager to decide which networks can coexist based on the hardware -->
+    <!-- An Array of "[radio-name],[priority]                        -->
+    <!--              [# simultaneous connection types]"             -->
+    <string-array translatable="false" name="radioAttributes">
+        <item>"wifi,1,1"</item>
+        <item>"mobile,0,1"</item>
+    </string-array>
 </resources>
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 62b839d..1c60058 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -30,48 +30,130 @@
 import android.net.wifi.WifiStateTracker;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
 
+import com.android.internal.telephony.Phone;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * @hide
  */
 public class ConnectivityService extends IConnectivityManager.Stub {
 
-    private static final boolean DBG = false;
+    private static final boolean DBG = true;
     private static final String TAG = "ConnectivityService";
 
     // Event log tags (must be in sync with event-log-tags)
     private static final int EVENTLOG_CONNECTIVITY_STATE_CHANGED = 50020;
 
+    // how long to wait before switching back to a radio's default network
+    private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000;
+    // system property that can override the above value
+    private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
+            "android.telephony.apn-restore";
+
     /**
      * Sometimes we want to refer to the individual network state
      * trackers separately, and sometimes we just want to treat them
      * abstractly.
      */
     private NetworkStateTracker mNetTrackers[];
-    private WifiStateTracker mWifiStateTracker;
-    private MobileDataStateTracker mMobileDataStateTracker;
+
+    /**
+     * 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
+    private int[] mPriorityList;
+
     private Context mContext;
     private int mNetworkPreference;
-    private NetworkStateTracker mActiveNetwork;
+    private int mActiveDefaultNetwork = -1;
 
     private int mNumDnsEntries;
-    private static int sDnsChangeCounter;
 
     private boolean mTestMode;
     private static ConnectivityService sServiceInstance;
 
+    private Handler mHandler;
+
+    // list of DeathRecipients used to make sure features are turned off when
+    // a process dies
+    private List mFeatureUsers;
+
+    private class NetworkAttributes {
+        /**
+         * Class for holding settings read from resources.
+         */
+        public String mName;
+        public int mType;
+        public int mRadio;
+        public int mPriority;
+        public NetworkAttributes(String init) {
+            String fragments[] = init.split(",");
+            mName = fragments[0].toLowerCase();
+            if (fragments[1].toLowerCase().equals("wifi")) {
+                mRadio = ConnectivityManager.TYPE_WIFI;
+            } else {
+                mRadio = ConnectivityManager.TYPE_MOBILE;
+            }
+            if (mName.equals("default")) {
+                mType = mRadio;
+            } else if (mName.equals("mms")) {
+                mType = ConnectivityManager.TYPE_MOBILE_MMS;
+            } else if (mName.equals("supl")) {
+                mType = ConnectivityManager.TYPE_MOBILE_SUPL;
+            } else if (mName.equals("dun")) {
+                mType = ConnectivityManager.TYPE_MOBILE_DUN;
+            } else if (mName.equals("hipri")) {
+                mType = ConnectivityManager.TYPE_MOBILE_HIPRI;
+            }
+            mPriority = Integer.parseInt(fragments[2]);
+        }
+        public boolean isDefault() {
+            return (mType == mRadio);
+        }
+    }
+    NetworkAttributes[] mNetAttributes;
+
+    private class RadioAttributes {
+        public String mName;
+        public int mPriority;
+        public int mSimultaneity;
+        public int mType;
+        public RadioAttributes(String init) {
+            String fragments[] = init.split(",");
+            mName = fragments[0].toLowerCase();
+            mPriority = Integer.parseInt(fragments[1]);
+            mSimultaneity = Integer.parseInt(fragments[2]);
+            if (mName.equals("wifi")) {
+                mType = ConnectivityManager.TYPE_WIFI;
+            } else {
+                mType = ConnectivityManager.TYPE_MOBILE;
+            }
+        }
+    }
+    RadioAttributes[] mRadioAttributes;
+
     private static class ConnectivityThread extends Thread {
         private Context mContext;
 
@@ -118,11 +200,54 @@
     private ConnectivityService(Context context) {
         if (DBG) Log.v(TAG, "ConnectivityService starting up");
         mContext = context;
-        mNetTrackers = new NetworkStateTracker[2];
-        Handler handler = new MyHandler();
+        mNetTrackers = new NetworkStateTracker[
+                ConnectivityManager.MAX_NETWORK_TYPE+1];
+        mHandler = new MyHandler();
 
         mNetworkPreference = getPersistedNetworkPreference();
 
+        // Load device network attributes from resources
+        mNetAttributes = new NetworkAttributes[
+                ConnectivityManager.MAX_NETWORK_TYPE+1];
+        mRadioAttributes = new RadioAttributes[
+                ConnectivityManager.MAX_RADIO_TYPE+1];
+        String[] naStrings = context.getResources().getStringArray(
+                com.android.internal.R.array.networkAttributes);
+        // TODO - what if the setting has gaps/unknown types?
+        for (String a : naStrings) {
+            NetworkAttributes n = new NetworkAttributes(a);
+            mNetAttributes[n.mType] = n;
+        }
+        String[] raStrings = context.getResources().getStringArray(
+                com.android.internal.R.array.radioAttributes);
+        for (String a : raStrings) {
+            RadioAttributes r = new RadioAttributes(a);
+            mRadioAttributes[r.mType] = r;
+        }
+
+        // high priority first
+        mPriorityList = new int[naStrings.length];
+        {
+            int priority = 0; //lowest
+            int nextPos = naStrings.length-1;
+            while (nextPos>-1) {
+                for (int i = 0; i < mNetAttributes.length; i++) {
+                    if(mNetAttributes[i].mPriority == priority) {
+                        mPriorityList[nextPos--] = i;
+                    }
+                }
+                priority++;
+            }
+        }
+
+        mNetRequestersPids =
+                new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1];
+        for (int i=0; i<=ConnectivityManager.MAX_NETWORK_TYPE; i++) {
+            mNetRequestersPids[i] = new ArrayList();
+        }
+
+        mFeatureUsers = new ArrayList();
+
         /*
          * Create the network state trackers for Wi-Fi and mobile
          * data. Maybe this could be done with a factory class,
@@ -131,15 +256,36 @@
          * to change very often.
          */
         if (DBG) Log.v(TAG, "Starting Wifi Service.");
-        mWifiStateTracker = new WifiStateTracker(context, handler);
-        WifiService wifiService = new WifiService(context, mWifiStateTracker);
+        WifiStateTracker wst = new WifiStateTracker(context, mHandler);
+        WifiService wifiService = new WifiService(context, wst);
         ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
-        mNetTrackers[ConnectivityManager.TYPE_WIFI] = mWifiStateTracker;
+        mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
 
-        mMobileDataStateTracker = new MobileDataStateTracker(context, handler);
-        mNetTrackers[ConnectivityManager.TYPE_MOBILE] = mMobileDataStateTracker;
+        mNetTrackers[ConnectivityManager.TYPE_MOBILE] =
+                new MobileDataStateTracker(context, mHandler,
+                ConnectivityManager.TYPE_MOBILE, Phone.APN_TYPE_DEFAULT,
+                "MOBILE");
 
-        mActiveNetwork = null;
+        mNetTrackers[ConnectivityManager.TYPE_MOBILE_MMS] =
+                new MobileDataStateTracker(context, mHandler,
+                ConnectivityManager.TYPE_MOBILE_MMS, Phone.APN_TYPE_MMS,
+                "MOBILE_MMS");
+
+        mNetTrackers[ConnectivityManager.TYPE_MOBILE_SUPL] =
+                new MobileDataStateTracker(context, mHandler,
+                ConnectivityManager.TYPE_MOBILE_SUPL, Phone.APN_TYPE_SUPL,
+                "MOBILE_SUPL");
+
+        mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] =
+                new MobileDataStateTracker(context, mHandler,
+                ConnectivityManager.TYPE_MOBILE_DUN, Phone.APN_TYPE_DUN,
+                "MOBILE_DUN");
+
+        mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI] =
+                new MobileDataStateTracker(context, mHandler,
+                ConnectivityManager.TYPE_MOBILE_HIPRI, Phone.APN_TYPE_HIPRI,
+                "MOBILE_HIPRI");
+
         mNumDnsEntries = 0;
 
         mTestMode = SystemProperties.get("cm.test.mode").equals("true")
@@ -149,8 +295,7 @@
             t.startMonitoring();
 
         // Constructing this starts it too
-        mWifiWatchdogService = new WifiWatchdogService(context,
-                mWifiStateTracker);
+        mWifiWatchdogService = new WifiWatchdogService(context, wst);
     }
 
     /**
@@ -159,7 +304,8 @@
      */
     public synchronized void setNetworkPreference(int preference) {
         enforceChangePermission();
-        if (ConnectivityManager.isNetworkTypeValid(preference)) {
+        if (ConnectivityManager.isNetworkTypeValid(preference) &&
+                mNetAttributes[preference].isDefault()) {
             if (mNetworkPreference != preference) {
                 persistNetworkPreference(preference);
                 mNetworkPreference = preference;
@@ -199,23 +345,16 @@
      * (see {@link #handleDisconnect(NetworkInfo)}).
      */
     private void enforcePreference() {
-        if (mActiveNetwork == null)
+        if (mNetTrackers[mNetworkPreference].getNetworkInfo().isConnected())
             return;
 
-        for (NetworkStateTracker t : mNetTrackers) {
-            if (t == mActiveNetwork) {
-                int netType = t.getNetworkInfo().getType();
-                int otherNetType = ((netType == ConnectivityManager.TYPE_WIFI) ?
-                        ConnectivityManager.TYPE_MOBILE :
-                        ConnectivityManager.TYPE_WIFI);
+        if (!mNetTrackers[mNetworkPreference].isAvailable())
+            return;
 
-                if (t.getNetworkInfo().getType() != mNetworkPreference) {
-                    NetworkStateTracker otherTracker =
-                            mNetTrackers[otherNetType];
-                    if (otherTracker.isAvailable()) {
-                        teardown(t);
-                    }
-                }
+        for (int t=0; t <= ConnectivityManager.MAX_RADIO_TYPE; t++) {
+            if (t != mNetworkPreference &&
+                    mNetTrackers[t].getNetworkInfo().isConnected()) {
+                teardown(mNetTrackers[t]);
             }
         }
     }
@@ -238,9 +377,16 @@
      */
     public NetworkInfo getActiveNetworkInfo() {
         enforceAccessPermission();
-        for (NetworkStateTracker t : mNetTrackers) {
+        for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
+            if (!mNetAttributes[type].isDefault()) {
+                continue;
+            }
+            NetworkStateTracker t = mNetTrackers[type];
             NetworkInfo info = t.getNetworkInfo();
             if (info.isConnected()) {
+                if (DBG && type != mActiveDefaultNetwork) Log.e(TAG,
+                        "connected default network is not " +
+                        "mActiveDefaultNetwork!");
                 return info;
             }
         }
@@ -285,30 +431,189 @@
         return tracker != null && tracker.setRadio(turnOn);
     }
 
-    public int startUsingNetworkFeature(int networkType, String feature) {
+    private class FeatureUser implements IBinder.DeathRecipient {
+        int mNetworkType;
+        String mFeature;
+        IBinder mBinder;
+        int mPid;
+        int mUid;
+
+        FeatureUser(int type, String feature, IBinder binder) {
+            super();
+            mNetworkType = type;
+            mFeature = feature;
+            mBinder = binder;
+            mPid = getCallingPid();
+            mUid = getCallingUid();
+            try {
+                mBinder.linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                binderDied();
+            }
+        }
+
+        void unlinkDeathRecipient() {
+            mBinder.unlinkToDeath(this, 0);
+        }
+
+        public void binderDied() {
+            Log.d(TAG, "ConnectivityService FeatureUser binderDied(" +
+                    mNetworkType + ", " + mFeature + ", " + mBinder);
+            stopUsingNetworkFeature(mNetworkType, mFeature, mPid, mUid);
+        }
+
+    }
+
+    public int startUsingNetworkFeature(int networkType, String feature,
+            IBinder binder) {
+        if (DBG) {
+            Log.d(TAG, "startUsingNetworkFeature for net " + networkType +
+                    ": " + feature);
+        }
         enforceChangePermission();
         if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
-            return -1;
+            return Phone.APN_REQUEST_FAILED;
         }
-        NetworkStateTracker tracker = mNetTrackers[networkType];
-        if (tracker != null) {
-            return tracker.startUsingNetworkFeature(feature, getCallingPid(),
-                    getCallingUid());
+
+        synchronized (mFeatureUsers) {
+            mFeatureUsers.add(new FeatureUser(networkType, feature, binder));
         }
-        return -1;
+
+        // TODO - move this into the 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;
+            }
+        }
+        NetworkStateTracker network = mNetTrackers[usedNetworkType];
+        if (network != null) {
+            if (usedNetworkType != networkType) {
+                Integer currentPid = new Integer(getCallingPid());
+
+                NetworkStateTracker radio = mNetTrackers[networkType];
+                NetworkInfo ni = network.getNetworkInfo();
+
+                if (ni.isAvailable() == false) {
+                    if (DBG) Log.d(TAG, "special network not available");
+                    return Phone.APN_TYPE_NOT_AVAILABLE;
+                }
+
+                if (!mNetRequestersPids[usedNetworkType].contains(currentPid)) {
+                    // this gets used for per-pid dns when connected
+                    mNetRequestersPids[usedNetworkType].add(currentPid);
+                }
+
+                if (ni.isConnectedOrConnecting() == true) {
+                    if (ni.isConnected() == true) {
+                        // add the pid-specific dns
+                        handleDnsConfigurationChange();
+                        if (DBG) Log.d(TAG, "special network already active");
+                        return Phone.APN_ALREADY_ACTIVE;
+                    }
+                    if (DBG) Log.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
+
+                // since we have to drop the default on this radio, setup
+                // an automatic event to switch back
+                if(mHandler.hasMessages(NetworkStateTracker.
+                        EVENT_RESTORE_DEFAULT_NETWORK, radio) ||
+                        radio.getNetworkInfo().isConnectedOrConnecting()) {
+                    mHandler.removeMessages(NetworkStateTracker.
+                            EVENT_RESTORE_DEFAULT_NETWORK,
+                            radio);
+                    mHandler.sendMessageDelayed(mHandler.obtainMessage(
+                            NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
+                            radio), getRestoreDefaultNetworkDelay());
+                }
+                if (DBG) Log.d(TAG, "reconnecting to special network");
+                network.reconnect();
+                return Phone.APN_REQUEST_STARTED;
+            } else {
+                return network.startUsingNetworkFeature(feature,
+                        getCallingPid(), getCallingUid());
+            }
+        }
+        return Phone.APN_TYPE_NOT_AVAILABLE;
     }
 
     public int stopUsingNetworkFeature(int networkType, String feature) {
+        return stopUsingNetworkFeature(networkType, feature, getCallingPid(),
+                getCallingUid());
+    }
+
+    private int stopUsingNetworkFeature(int networkType, String feature,
+            int pid, int uid) {
+        if (DBG) {
+            Log.d(TAG, "stopUsingNetworkFeature for net " + networkType +
+                    ": " + feature);
+        }
         enforceChangePermission();
         if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
             return -1;
         }
-        NetworkStateTracker tracker = mNetTrackers[networkType];
-        if (tracker != null) {
-            return tracker.stopUsingNetworkFeature(feature, getCallingPid(),
-                    getCallingUid());
+
+        synchronized (mFeatureUsers) {
+            for (int i=0; i < mFeatureUsers.size(); i++) {
+                FeatureUser u = (FeatureUser)mFeatureUsers.get(i);
+                if (uid == u.mUid && pid == u.mPid &&
+                        networkType == u.mNetworkType &&
+                        TextUtils.equals(feature, u.mFeature)) {
+                    u.unlinkDeathRecipient();
+                    mFeatureUsers.remove(i);
+                    break;
+                }
+            }
         }
-        return -1;
+
+        // 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;
+            }
+        }
+        NetworkStateTracker tracker =  mNetTrackers[usedNetworkType];
+        if(usedNetworkType != networkType) {
+            Integer currentPid = new Integer(pid);
+            if (mNetRequestersPids[usedNetworkType].remove(currentPid)) {
+                reassessPidDns(pid, true);
+            }
+            if (mNetRequestersPids[usedNetworkType].size() != 0) {
+                if (DBG) Log.d(TAG, "not tearing down special network - " +
+                        "others still using it");
+                return 1;
+            }
+
+            tracker.teardown();
+            NetworkStateTracker radio = mNetTrackers[networkType];
+            // Check if we want to revert to the default
+            if (mHandler.hasMessages(NetworkStateTracker.
+                    EVENT_RESTORE_DEFAULT_NETWORK, radio)) {
+                mHandler.removeMessages(NetworkStateTracker.
+                        EVENT_RESTORE_DEFAULT_NETWORK, radio);
+                radio.reconnect();
+            }
+            return 1;
+        } else {
+            return tracker.stopUsingNetworkFeature(feature, pid, uid);
+        }
     }
 
     /**
@@ -337,7 +642,8 @@
         if (getNumConnectedNetworks() > 1) {
             return tracker.requestRouteToHost(hostAddress);
         } else {
-            return tracker.getNetworkInfo().getType() == networkType;
+            return (mNetAttributes[networkType].isDefault() &&
+                    tracker.getNetworkInfo().isConnected());
         }
     }
 
@@ -402,45 +708,26 @@
     private void handleDisconnect(NetworkInfo info) {
 
         if (DBG) Log.v(TAG, "Handle DISCONNECT for " + info.getTypeName());
+        int prevNetType = info.getType();
 
-        mNetTrackers[info.getType()].setTeardownRequested(false);
+        mNetTrackers[prevNetType].setTeardownRequested(false);
         /*
          * If the disconnected network is not the active one, then don't report
          * this as a loss of connectivity. What probably happened is that we're
          * getting the disconnect for a network that we explicitly disabled
          * in accordance with network preference policies.
          */
-        if (mActiveNetwork == null ||
-                info.getType() != mActiveNetwork.getNetworkInfo().getType())
-            return;
-
-        NetworkStateTracker newNet;
-        if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
-            newNet = mWifiStateTracker;
-        } else /* info().getType() == TYPE_WIFI */ {
-            newNet = mMobileDataStateTracker;
-        }
-
-        /**
-         * See if the other network is available to fail over to.
-         * If is not available, we enable it anyway, so that it
-         * will be able to connect when it does become available,
-         * but we report a total loss of connectivity rather than
-         * report that we are attempting to fail over.
-         */
-        NetworkInfo switchTo = null;
-        if (newNet.isAvailable()) {
-            mActiveNetwork = newNet;
-            switchTo = newNet.getNetworkInfo();
-            switchTo.setFailover(true);
-            if (!switchTo.isConnectedOrConnecting()) {
-                newNet.reconnect();
+        if (!mNetAttributes[prevNetType].isDefault()) {
+            List pids = mNetRequestersPids[prevNetType];
+            for (int i = 0; i<pids.size(); i++) {
+                Integer pid = (Integer)pids.get(i);
+                // will remove them because the net's no longer connected
+                // need to do this now as only now do we know the pids and
+                // can properly null things that are no longer referenced.
+                reassessPidDns(pid.intValue(), false);
             }
-        } else {
-            newNet.reconnect();
         }
 
-        boolean otherNetworkConnected = false;
         Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
         intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
         if (info.isFailover()) {
@@ -454,33 +741,92 @@
             intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
                     info.getExtraInfo());
         }
-        if (switchTo != null) {
-            otherNetworkConnected = switchTo.isConnected();
-            if (DBG) {
-                if (otherNetworkConnected) {
-                    Log.v(TAG, "Switching to already connected " +
-                            switchTo.getTypeName());
-                } else {
-                    Log.v(TAG, "Attempting to switch to " +
-                            switchTo.getTypeName());
+
+        /*
+         * If this is a default network, check if other defaults are available
+         * or active
+         */
+        NetworkStateTracker newNet = null;
+        if (mNetAttributes[prevNetType].isDefault()) {
+            if (DBG) Log.d(TAG, "disconnecting a default network");
+            if (mActiveDefaultNetwork == prevNetType) {
+                mActiveDefaultNetwork = -1;
+            }
+
+            int newType = -1;
+            int newPriority = -1;
+            for (int checkType=0; checkType <=
+                    ConnectivityManager.MAX_NETWORK_TYPE; checkType++) {
+                if (checkType == prevNetType) {
+                    continue;
+                }
+                if (mNetAttributes[checkType].isDefault()) {
+                    /* TODO - if we have multiple nets we could use
+                     * we may want to put more thought into which we choose
+                     */
+                    if (checkType == mNetworkPreference) {
+                        newType = checkType;
+                        break;
+                    }
+                    if (mRadioAttributes[mNetAttributes[checkType].mRadio].
+                            mPriority > newPriority) {
+                        newType = checkType;
+                        newPriority = mRadioAttributes[mNetAttributes[newType].
+                                mRadio].mPriority;
+                    }
                 }
             }
-            intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
-                    switchTo);
-        } else {
-            intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
+
+            if (newType != -1) {
+                newNet = mNetTrackers[newType];
+                /**
+                 * See if the other network is available to fail over to.
+                 * If is not available, we enable it anyway, so that it
+                 * will be able to connect when it does become available,
+                 * but we report a total loss of connectivity rather than
+                 * report that we are attempting to fail over.
+                 */
+                if (newNet.isAvailable()) {
+                    NetworkInfo switchTo = newNet.getNetworkInfo();
+                    switchTo.setFailover(true);
+                    if (!switchTo.isConnectedOrConnecting()) {
+                        newNet.reconnect();
+                    }
+                    if (DBG) {
+                        if (switchTo.isConnected()) {
+                            Log.v(TAG, "Switching to already connected " +
+                                    switchTo.getTypeName());
+                        } else {
+                            Log.v(TAG, "Attempting to switch to " +
+                                    switchTo.getTypeName());
+                        }
+                    }
+                    intent.putExtra(ConnectivityManager.
+                            EXTRA_OTHER_NETWORK_INFO, switchTo);
+                } else {
+                    newNet.reconnect();
+                }
+            } else {
+                intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY,
+                        true);
+            }
         }
+
+        // do this before we broadcast the change
+        handleConnectivityChange();
+
         if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " +
                 info.getTypeName() +
-                (switchTo == null ? "" : " other=" + switchTo.getTypeName()));
+                (newNet == null || !newNet.isAvailable() ? "" : " other=" +
+                newNet.getNetworkInfo().getTypeName()));
 
         mContext.sendStickyBroadcast(intent);
         /*
          * If the failover network is already connected, then immediately send
          * out a followup broadcast indicating successful failover
          */
-        if (switchTo != null && otherNetworkConnected)
-            sendConnectedBroadcast(switchTo);
+        if (newNet != null && newNet.getNetworkInfo().isConnected())
+            sendConnectedBroadcast(newNet.getNetworkInfo());
     }
 
     private void sendConnectedBroadcast(NetworkInfo info) {
@@ -506,93 +852,85 @@
      */
     private void handleConnectionFailure(NetworkInfo info) {
         mNetTrackers[info.getType()].setTeardownRequested(false);
-        if (getActiveNetworkInfo() == null) {
-            String reason = info.getReason();
-            String extraInfo = info.getExtraInfo();
 
-            if (DBG) {
-                String reasonText;
-                if (reason == null) {
-                    reasonText = ".";
-                } else {
-                    reasonText = " (" + reason + ").";
-                }
-                Log.v(TAG, "Attempt to connect to " + info.getTypeName() +
-                        " failed" + reasonText);
-            }
+        String reason = info.getReason();
+        String extraInfo = info.getExtraInfo();
 
-            Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
-            intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
-            intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
-            if (reason != null) {
-                intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
+        if (DBG) {
+            String reasonText;
+            if (reason == null) {
+                reasonText = ".";
+            } else {
+                reasonText = " (" + reason + ").";
             }
-            if (extraInfo != null) {
-                intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
-                        extraInfo);
-            }
-            if (info.isFailover()) {
-                intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
-                info.setFailover(false);
-            }
-            mContext.sendStickyBroadcast(intent);
+            Log.v(TAG, "Attempt to connect to " + info.getTypeName() +
+                    " failed" + reasonText);
         }
+
+        Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+        if (getActiveNetworkInfo() == null) {
+            intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
+        }
+        if (reason != null) {
+            intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
+        }
+        if (extraInfo != null) {
+            intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo);
+        }
+        if (info.isFailover()) {
+            intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
+            info.setFailover(false);
+        }
+        mContext.sendStickyBroadcast(intent);
     }
 
     private void handleConnect(NetworkInfo info) {
-        if (DBG) Log.v(TAG, "Handle CONNECT for " + info.getTypeName());
+        if (DBG) Log.d(TAG, "Handle CONNECT for " + info.getTypeName());
+
+        int type = info.getType();
 
         // snapshot isFailover, because sendConnectedBroadcast() resets it
         boolean isFailover = info.isFailover();
-        NetworkStateTracker thisNet = mNetTrackers[info.getType()];
-        NetworkStateTracker deadnet = null;
-        NetworkStateTracker otherNet;
-        if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
-            otherNet = mWifiStateTracker;
-        } else /* info().getType() == TYPE_WIFI */ {
-            otherNet = mMobileDataStateTracker;
-        }
-        /*
-         * Check policy to see whether we are connected to a non-preferred
-         * network that now needs to be torn down.
-         */
-        NetworkInfo wifiInfo = mWifiStateTracker.getNetworkInfo();
-        NetworkInfo mobileInfo = mMobileDataStateTracker.getNetworkInfo();
-        if (wifiInfo.isConnected() && mobileInfo.isConnected()) {
-            if (mNetworkPreference == ConnectivityManager.TYPE_WIFI)
-                deadnet = mMobileDataStateTracker;
-            else
-                deadnet = mWifiStateTracker;
-        }
+        NetworkStateTracker thisNet = mNetTrackers[type];
 
-        boolean toredown = false;
+        // if this is a default net and other default is running
+        // kill the one not preferred
+        if (mNetAttributes[type].isDefault()) {
+            if (DBG) Log.d(TAG, "connecting to a default network");
+            if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
+                if ((type != mNetworkPreference &&
+                        mNetAttributes[mActiveDefaultNetwork].mPriority >
+                        mNetAttributes[type].mPriority) ||
+                        mNetworkPreference == mActiveDefaultNetwork) {
+                        // don't accept this one
+                        if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION " +
+                                "to torn down network " + info.getTypeName());
+                        teardown(thisNet);
+                        return;
+                } else {
+                    // tear down the other
+                    NetworkStateTracker otherNet =
+                            mNetTrackers[mActiveDefaultNetwork];
+                    if (DBG) Log.v(TAG, "Policy requires " +
+                            otherNet.getNetworkInfo().getTypeName() +
+                            " teardown");
+                    if (!teardown(otherNet)) {
+                        Log.e(TAG, "Network declined teardown request");
+                        return;
+                    }
+                    if (isFailover) {
+                        otherNet.releaseWakeLock();
+                    }
+                }
+            }
+            mActiveDefaultNetwork = type;
+        }
         thisNet.setTeardownRequested(false);
-        if (!mTestMode && deadnet != null) {
-            if (DBG) Log.v(TAG, "Policy requires " +
-                  deadnet.getNetworkInfo().getTypeName() + " teardown");
-            toredown = teardown(deadnet);
-            if (DBG && !toredown) {
-                Log.d(TAG, "Network declined teardown request");
-            }
-        }
-
-        /*
-         * Note that if toredown is true, deadnet cannot be null, so there is
-         * no danger of a null pointer exception here..
-         */
-        if (!toredown || deadnet.getNetworkInfo().getType() != info.getType()) {
-            mActiveNetwork = thisNet;
-            if (DBG) Log.v(TAG, "Sending CONNECT bcast for " +
-                    info.getTypeName());
-            thisNet.updateNetworkSettings();
-            sendConnectedBroadcast(info);
-            if (isFailover) {
-                otherNet.releaseWakeLock();
-            }
-        } else {
-            if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION to torn " +
-                    "down network " + info.getTypeName());
-        }
+        if (DBG) Log.d(TAG, "Sending CONNECT bcast for " + info.getTypeName());
+        thisNet.updateNetworkSettings();
+        handleConnectivityChange();
+        sendConnectedBroadcast(info);
     }
 
     private void handleScanResultsAvailable(NetworkInfo info) {
@@ -626,60 +964,148 @@
      * table entries exist.
      */
     private void handleConnectivityChange() {
+        if (DBG) Log.d(TAG, "handleConnectivityChange");
         /*
+         * If a non-default network is enabled, add the host routes that
+         * will allow it's DNS servers to be accessed.  Only 
          * If both mobile and wifi are enabled, add the host routes that
          * will allow MMS traffic to pass on the mobile network. But
          * remove the default route for the mobile network, so that there
          * will be only one default route, to ensure that all traffic
          * except MMS will travel via Wi-Fi.
          */
-        int numConnectedNets = handleConfigurationChange();
-        if (numConnectedNets > 1) {
-            mMobileDataStateTracker.addPrivateRoutes();
-            mMobileDataStateTracker.removeDefaultRoute();
-        } else if (mMobileDataStateTracker.getNetworkInfo().isConnected()) {
-            mMobileDataStateTracker.removePrivateRoutes();
-            mMobileDataStateTracker.restoreDefaultRoute();
+        handleDnsConfigurationChange();
+
+        for (int netType : mPriorityList) {
+            if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
+                if (mNetAttributes[netType].isDefault()) {
+                    mNetTrackers[netType].addDefaultRoute();
+                } else {
+                    mNetTrackers[netType].addPrivateDnsRoutes();
+                }
+            } else {
+                if (mNetAttributes[netType].isDefault()) {
+                    mNetTrackers[netType].removeDefaultRoute();
+                } else {
+                    mNetTrackers[netType].removePrivateDnsRoutes();
+                }
+            }
         }
     }
 
-    private int handleConfigurationChange() {
-        /*
-         * Set DNS properties. Always put Wi-Fi entries at the front of
-         * the list if it is active.
-         */
-        int index = 1;
-        String lastDns = "";
-        int numConnectedNets = 0;
-        int incrValue = ConnectivityManager.TYPE_MOBILE -
-                ConnectivityManager.TYPE_WIFI;
-        int stopValue = ConnectivityManager.TYPE_MOBILE + incrValue;
-
-        for (int netType = ConnectivityManager.TYPE_WIFI; netType != stopValue;
-                netType += incrValue) {
-            NetworkStateTracker nt = mNetTrackers[netType];
+    /**
+     * 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) Log.d(TAG, "reassessPidDns for pid " + myPid);
+        for(int i : mPriorityList) {
+            if (mNetAttributes[i].isDefault()) {
+                continue;
+            }
+            NetworkStateTracker nt = mNetTrackers[i];
             if (nt.getNetworkInfo().isConnected() &&
                     !nt.isTeardownRequested()) {
-                ++numConnectedNets;
+                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);
+                        if (doBump) {
+                            bumpDns();
+                        }
+                        return;
+                    }
+                }
+           }
+        }
+        // nothing found - delete
+        for (int i = 1; ; i++) {
+            String prop = "net.dns" + i + "." + myPid;
+            if (SystemProperties.get(prop).length() == 0) {
+                if (doBump) {
+                    bumpDns();
+                }
+                return;
+            }
+            SystemProperties.set(prop, "");
+        }
+    }
+
+    private void writePidDns(String[] dnsList, 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);
+            }
+        }
+    }
+
+    private void bumpDns() {
+        /*
+         * Bump the property that tells the name resolver library to reread
+         * the DNS server list from the properties.
+         */
+        String propVal = SystemProperties.get("net.dnschange");
+        int n = 0;
+        if (propVal.length() != 0) {
+            try {
+                n = Integer.parseInt(propVal);
+            } catch (NumberFormatException e) {}
+        }
+        SystemProperties.set("net.dnschange", "" + (n+1));
+    }
+
+    private void handleDnsConfigurationChange() {
+        if (DBG) Log.d(TAG, "handleDnsConfig Change");
+        // add default net's dns entries
+        for (int x = mPriorityList.length-1; x>= 0; x--) {
+            int netType = mPriorityList[x];
+            NetworkStateTracker nt = mNetTrackers[netType];
+            if (DBG) Log.d(TAG, " checking " + nt);
+            if (nt != null && nt.getNetworkInfo().isConnected() &&
+                    !nt.isTeardownRequested()) {
+                if (DBG) Log.d(TAG, "  connected");
                 String[] dnsList = nt.getNameServers();
-                for (int i = 0; i < dnsList.length && dnsList[i] != null; i++) {
-                    // skip duplicate entries
-                    if (!dnsList[i].equals(lastDns)) {
-                        SystemProperties.set("net.dns" + index++, dnsList[i]);
-                        lastDns = dnsList[i];
+                if (mNetAttributes[netType].isDefault()) {
+                    int j = 1;
+                    for (String dns : dnsList) {
+                        if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
+                            SystemProperties.set("net.dns" + j++, dns);
+                        }
+                    }
+                    for (int k=j ; k<mNumDnsEntries; k++) {
+                        SystemProperties.set("net.dns" + j, "");
+                    }
+                    mNumDnsEntries = j;
+                } else {
+                    // set per-pid dns for attached secondary nets
+                    List pids = mNetRequestersPids[netType];
+                    for (int y=0; y< pids.size(); y++) {
+                        Integer pid = (Integer)pids.get(y);
+                        writePidDns(dnsList, pid.intValue());
                     }
                 }
             }
         }
-        // Null out any DNS properties that are no longer used
-        for (int i = index; i <= mNumDnsEntries; i++) {
-            SystemProperties.set("net.dns" + i, "");
+
+        bumpDns();
+    }
+
+    private int getRestoreDefaultNetworkDelay() {
+        String restoreDefaultNetworkDelayStr = SystemProperties.get(
+                NETWORK_RESTORE_DELAY_PROP_NAME);
+        if(restoreDefaultNetworkDelayStr != null &&
+                restoreDefaultNetworkDelayStr.length() != 0) {
+            try {
+                return Integer.valueOf(restoreDefaultNetworkDelayStr);
+            } catch (NumberFormatException e) {
+            }
         }
-        mNumDnsEntries = index - 1;
-        // Notify the name resolver library of the change
-        SystemProperties.set("net.dnschange",
-                String.valueOf(sDnsChangeCounter++));
-        return numConnectedNets;
+        return RESTORE_DEFAULT_NETWORK_DELAY;
     }
 
     @Override
@@ -692,20 +1118,19 @@
                     Binder.getCallingUid());
             return;
         }
-        if (mActiveNetwork == null) {
-            pw.println("No active network");
-        } else {
-            pw.println("Active network: " +
-                    mActiveNetwork.getNetworkInfo().getTypeName());
-        }
         pw.println();
         for (NetworkStateTracker nst : mNetTrackers) {
+            if (nst.getNetworkInfo().isConnected()) {
+                pw.println("Active network: " + nst.getNetworkInfo().
+                        getTypeName());
+            }
             pw.println(nst.getNetworkInfo());
             pw.println(nst);
             pw.println();
         }
     }
 
+    // must be stateless - things change under us.
     private class MyHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
@@ -713,7 +1138,7 @@
             switch (msg.what) {
                 case NetworkStateTracker.EVENT_STATE_CHANGED:
                     info = (NetworkInfo) msg.obj;
-                    if (DBG) Log.v(TAG, "ConnectivityChange for " +
+                    if (DBG) Log.d(TAG, "ConnectivityChange for " +
                             info.getTypeName() + ": " +
                             info.getState() + "/" + info.getDetailedState());
 
@@ -748,7 +1173,6 @@
                     } else if (info.getState() == NetworkInfo.State.CONNECTED) {
                         handleConnect(info);
                     }
-                    handleConnectivityChange();
                     break;
 
                 case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
@@ -761,7 +1185,7 @@
                             (Notification) msg.obj);
 
                 case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
-                    handleConfigurationChange();
+                    handleDnsConfigurationChange();
                     break;
 
                 case NetworkStateTracker.EVENT_ROAMING_CHANGED:
@@ -771,6 +1195,15 @@
                 case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
                     // fill me in
                     break;
+                case NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK:
+                    for (NetworkStateTracker net : mNetTrackers) {
+                        NetworkInfo i = net.getNetworkInfo();
+                        if (i.isConnected() &&
+                                !mNetAttributes[i.getType()].isDefault()) {
+                            teardown(net);
+                        }
+                    }
+                    break;
             }
         }
     }
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 9f2856c..7379b5d 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -88,6 +88,8 @@
 
     private String mDataConnectionApn = "";
 
+    private String[] mDataConnectionApnTypes = null;
+
     private String mDataConnectionInterfaceName = "";
 
     private Bundle mCellLocation = new Bundle();
@@ -337,7 +339,7 @@
     }
 
     public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
-            String reason, String apn, String interfaceName) {
+            String reason, String apn, String[] apnTypes, String interfaceName) {
         if (!checkNotifyPermission("notifyDataConnection()" )) {
             return;
         }
@@ -346,6 +348,7 @@
             mDataConnectionPossible = isDataConnectivityPossible;
             mDataConnectionReason = reason;
             mDataConnectionApn = apn;
+            mDataConnectionApnTypes = apnTypes;
             mDataConnectionInterfaceName = interfaceName;
             for (int i = mRecords.size() - 1; i >= 0; i--) {
                 Record r = mRecords.get(i);
@@ -359,7 +362,7 @@
             }
         }
         broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
-                interfaceName);
+                apnTypes, interfaceName);
     }
 
     public void notifyDataConnectionFailed(String reason) {
@@ -517,8 +520,9 @@
         mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE);
     }
 
-    private void broadcastDataConnectionStateChanged(int state, boolean isDataConnectivityPossible,
-            String reason, String apn, String interfaceName) {
+    private void broadcastDataConnectionStateChanged(int state,
+            boolean isDataConnectivityPossible,
+            String reason, String apn, String[] apnTypes, String interfaceName) {
         // Note: not reporting to the battery stats service here, because the
         // status bar takes care of that after taking into account all of the
         // required info.
@@ -531,6 +535,11 @@
             intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, reason);
         }
         intent.putExtra(Phone.DATA_APN_KEY, apn);
+        String types = apnTypes[0];
+        for (int i = 1; i < apnTypes.length; i++) {
+            types = types+","+apnTypes[i];
+        }
+        intent.putExtra(Phone.DATA_APN_TYPES_KEY, types);
         intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName);
         mContext.sendStickyBroadcast(intent);
     }
diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index d6151c6..00d7c72 100644
--- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -94,8 +94,11 @@
 
     public void notifyDataConnection(Phone sender, String reason) {
         try {
-            mRegistry.notifyDataConnection(convertDataState(sender.getDataConnectionState()),
-                    sender.isDataConnectivityPossible(), reason, sender.getActiveApn(),
+            mRegistry.notifyDataConnection(
+                    convertDataState(sender.getDataConnectionState()),
+                    sender.isDataConnectivityPossible(), reason,
+                    sender.getActiveApn(),
+                    sender.getActiveApnTypes(),
                     sender.getInterfaceName(null));
         } catch (RemoteException ex) {
             // system process is dead
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 865c6ca..6b42e6b 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -32,7 +32,7 @@
     void notifyCallForwardingChanged(boolean cfi);
     void notifyDataActivity(int state);
     void notifyDataConnection(int state, boolean isDataConnectivityPossible,
-            String reason, String apn, String interfaceName);
+            String reason, String apn, in String[] apnTypes, String interfaceName);
     void notifyDataConnectionFailed(String reason);
     void notifyCellLocation(in Bundle cellLocation);
 }
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 86ea12b..622b47d 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -99,8 +99,9 @@
     static final String PHONE_NAME_KEY = "phoneName";
     static final String FAILURE_REASON_KEY = "reason";
     static final String STATE_CHANGE_REASON_KEY = "reason";
-    static final String DATA_APN_TYPE_KEY = "apnType";
+    static final String DATA_APN_TYPES_KEY = "apnType";
     static final String DATA_APN_KEY = "apn";
+
     static final String DATA_IFACE_NAME_KEY = "iface";
     static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable";
     static final String PHONE_IN_ECM_STATE = "phoneinECMState";
@@ -120,10 +121,16 @@
     static final String APN_TYPE_MMS = "mms";
     /** APN type for SUPL assisted GPS */
     static final String APN_TYPE_SUPL = "supl";
+    /** APN type for DUN traffic */
+    static final String APN_TYPE_DUN = "dun";
+    /** APN type for HiPri traffic */
+    static final String APN_TYPE_HIPRI = "hipri";
 
     // "Features" accessible through the connectivity manager
     static final String FEATURE_ENABLE_MMS = "enableMMS";
     static final String FEATURE_ENABLE_SUPL = "enableSUPL";
+    static final String FEATURE_ENABLE_DUN = "enableDUN";
+    static final String FEATURE_ENABLE_HIPRI = "enableHIPRI";
 
     /**
      * Return codes for <code>enableApnType()</code>
diff --git a/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java b/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
index 3ca39de65..dc6f92d 100644
--- a/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
+++ b/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
@@ -72,7 +72,10 @@
 
     boolean canHandleType(String type) {
         for (String t : types) {
-            if (t.equals(type) || t.equals(Phone.APN_TYPE_ALL)) {
+            // DEFAULT handles all, and HIPRI is handled by DEFAULT
+            if (t.equals(type) || t.equals(Phone.APN_TYPE_ALL) ||
+                    (t.equals(Phone.APN_TYPE_DEFAULT) &&
+                    type.equals(Phone.APN_TYPE_HIPRI))) {
                 return true;
             }
         }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 8b3529f..bf60bfe 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -135,12 +135,16 @@
     /** Currently active PdpConnection */
     private PdpConnection mActivePdp;
 
+    private static int APN_INVALID_ID = -1;
     private static int APN_DEFAULT_ID = 0;
     private static int APN_MMS_ID = 1;
     private static int APN_SUPL_ID = 2;
-    private static int APN_NUM_TYPES = 3;
+    private static int APN_DUN_ID = 3;
+    private static int APN_HIPRI_ID = 4;
+    private static int APN_NUM_TYPES = 5;
 
     private boolean[] dataEnabled = new boolean[APN_NUM_TYPES];
+    private int enabledCount = 0;
 
     /** Is packet service restricted by network */
     private boolean mIsPsRestricted = false;
@@ -213,7 +217,6 @@
     GsmDataConnectionTracker(GSMPhone p) {
         super(p);
         mGsmPhone = p;
-
         p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null);
         p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
         p.mSIMRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
@@ -252,6 +255,9 @@
         // and 2) whether the RIL will setup the baseband to auto-PS attach.
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext());
         dataEnabled[APN_DEFAULT_ID] = !sp.getBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, false);
+        if (dataEnabled[APN_DEFAULT_ID]) {
+            enabledCount++;
+        }
         noAutoAttach = !dataEnabled[APN_DEFAULT_ID];
 
         if (!mRetryMgr.configure(SystemProperties.get("ro.gsm.data_retry_config"))) {
@@ -332,6 +338,22 @@
         return result;
     }
 
+    protected int apnTypeToId(String type) {
+        if (TextUtils.equals(type, Phone.APN_TYPE_DEFAULT)) {
+            return APN_DEFAULT_ID;
+        } else if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) {
+            return APN_MMS_ID;
+        } else if (TextUtils.equals(type, Phone.APN_TYPE_SUPL)) {
+            return APN_SUPL_ID;
+        } else if (TextUtils.equals(type, Phone.APN_TYPE_DUN)) {
+            return APN_DUN_ID;
+        } else if (TextUtils.equals(type, Phone.APN_TYPE_HIPRI)) {
+            return APN_HIPRI_ID;
+        } else {
+            return APN_INVALID_ID;
+        }
+    }
+
     /**
      * Ensure that we are connected to an APN of the specified type.
      * @param type the APN type (currently the only valid values
@@ -343,25 +365,15 @@
      * the APN has been established.
      */
     protected int enableApnType(String type) {
-        if (!TextUtils.equals(type, Phone.APN_TYPE_MMS) &&
-                !TextUtils.equals(type, Phone.APN_TYPE_SUPL)) {
+        int id = apnTypeToId(type);
+        if (id == APN_INVALID_ID) {
             return Phone.APN_REQUEST_FAILED;
         }
 
         // If already active, return
-        Log.d(LOG_TAG, "enableApnType("+type+")");
+        if(DBG) Log.d(LOG_TAG, "enableApnType("+type+"), isApnTypeActive = "
+                + isApnTypeActive(type) + " and state = " + state);
         if (isApnTypeActive(type)) {
-            setEnabled(type, true);
-            removeMessages(EVENT_RESTORE_DEFAULT_APN);
-            /**
-             * We're being asked to enable a non-default APN that's already in use.
-             * This means we should restart the timer that will automatically
-             * switch back to the default APN and disable the non-default APN
-             * when it expires.
-             */
-            sendMessageDelayed(
-                    obtainMessage(EVENT_RESTORE_DEFAULT_APN),
-                    getRestoreDefaultApnDelay());
             if (state == State.INITING) return Phone.APN_REQUEST_STARTED;
             else if (state == State.CONNECTED) return Phone.APN_ALREADY_ACTIVE;
         }
@@ -370,7 +382,7 @@
             return Phone.APN_TYPE_NOT_AVAILABLE;
         }
 
-        setEnabled(type, true);
+        setEnabled(id, true);
         mRequestedApnType = type;
         sendMessage(obtainMessage(EVENT_ENABLE_NEW_APN));
         return Phone.APN_REQUEST_STARTED;
@@ -385,31 +397,21 @@
      * @return
      */
     protected int disableApnType(String type) {
-        Log.d(LOG_TAG, "disableApnType("+type+")");
-        if ((TextUtils.equals(type, Phone.APN_TYPE_MMS) ||
-                TextUtils.equals(type, Phone.APN_TYPE_SUPL))
-                && isEnabled(type)) {
-            removeMessages(EVENT_RESTORE_DEFAULT_APN);
-            setEnabled(type, false);
+        if (DBG) Log.d(LOG_TAG, "disableApnType("+type+")");
+        int id = apnTypeToId(type);
+        if (id == APN_INVALID_ID) {
+            return Phone.APN_REQUEST_FAILED;
+        }
+        if (isEnabled(id)) {
+            setEnabled(id, false);
             if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
                 mRequestedApnType = Phone.APN_TYPE_DEFAULT;
                 if (dataEnabled[APN_DEFAULT_ID]) {
                     return Phone.APN_ALREADY_ACTIVE;
                 } else {
-                    Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
-                    msg.arg1 = 1; // tearDown is true;
-                    msg.obj = Phone.REASON_DATA_DISABLED;
-                    sendMessage(msg);
                     return Phone.APN_REQUEST_STARTED;
                 }
             } else {
-                /*
-                 * Note that if default data is disabled, the following
-                 * has the effect of disabling the MMS APN, and then
-                 * ignoring the request to enable the default APN.
-                 * The net result is that data is completely disabled.
-                 */
-                sendMessage(obtainMessage(EVENT_RESTORE_DEFAULT_APN));
                 return Phone.APN_REQUEST_STARTED;
             }
         } else {
@@ -455,30 +457,30 @@
         return false;
     }
 
-    private boolean isEnabled(String apnType) {
-        if (TextUtils.equals(apnType, Phone.APN_TYPE_DEFAULT)) {
-            return dataEnabled[APN_DEFAULT_ID];
-        } else if (TextUtils.equals(apnType, Phone.APN_TYPE_MMS)) {
-            return dataEnabled[APN_MMS_ID];
-        } else if (TextUtils.equals(apnType, Phone.APN_TYPE_SUPL)) {
-            return dataEnabled[APN_SUPL_ID];
-        } else {
-            return false;
+    private boolean isEnabled(int id) {
+        if (id != APN_INVALID_ID) {
+            return dataEnabled[id];
         }
+        return false;
     }
 
-    private void setEnabled(String apnType, boolean enable) {
-        Log.d(LOG_TAG, "setEnabled(" + apnType + ", " + enable + ')');
-        if (TextUtils.equals(apnType, Phone.APN_TYPE_DEFAULT)) {
-            dataEnabled[APN_DEFAULT_ID] = enable;
-        } else if (TextUtils.equals(apnType, Phone.APN_TYPE_MMS)) {
-            dataEnabled[APN_MMS_ID] = enable;
-        } else if (TextUtils.equals(apnType, Phone.APN_TYPE_SUPL)) {
-            dataEnabled[APN_SUPL_ID] = enable;
+    private void setEnabled(int id, boolean enable) {
+        if (DBG) Log.d(LOG_TAG, "setEnabled(" + id + ", " + enable + ')');
+        if (dataEnabled[id] != enable) {
+            dataEnabled[id] = enable;
+
+            if (enable) {
+                enabledCount++;
+            } else {
+                enabledCount--;
+            }
+
+            if (enabledCount == 0) {
+                setPrivateDataEnabled(false);
+            } else if (enabledCount == 1) {
+                setPrivateDataEnabled(true);
+            }
         }
-        Log.d(LOG_TAG, "dataEnabled[DEFAULT_APN]=" + dataEnabled[APN_DEFAULT_ID] +
-                " dataEnabled[MMS_APN]=" + dataEnabled[APN_MMS_ID] +
-                " dataEnabled[SUPL_APN]=" + dataEnabled[APN_SUPL_ID]);
     }
 
     /**
@@ -493,30 +495,20 @@
      * @return {@code true} if the operation succeeded
      */
     public boolean setDataEnabled(boolean enable) {
-        boolean isEnabled = isEnabled(Phone.APN_TYPE_DEFAULT);
-        Log.d(LOG_TAG, "setDataEnabled("+enable+") isEnabled=" + isEnabled);
-        if (!isEnabled && enable) {
-            setEnabled(Phone.APN_TYPE_DEFAULT, true);
-            // trySetupData() will be a no-op if we are currently
-            // connected to the MMS APN
+        if (DBG) Log.d(LOG_TAG, "setDataEnabled("+enable+")");
+        setEnabled(APN_DEFAULT_ID, enable);
+        return true;
+    }
+
+    private void setPrivateDataEnabled(boolean enable) {
+        if (DBG) Log.d(LOG_TAG, "setPrivateDataEnabled("+enable+")");
+        if (enable) {
             sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA));
-            return true;
-        } else if (!enable) {
-            setEnabled(Phone.APN_TYPE_DEFAULT, false);
-            // Don't tear down if there is an active APN and it handles MMS or SUPL.
-            // TODO: This isn't very general.
-            if ((isApnTypeActive(Phone.APN_TYPE_MMS) && isEnabled(Phone.APN_TYPE_MMS)) ||
-                (isApnTypeActive(Phone.APN_TYPE_SUPL) && isEnabled(Phone.APN_TYPE_SUPL))) {
-                return false;
-            }
+        } else {
             Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
             msg.arg1 = 1; // tearDown is true
             msg.obj = Phone.REASON_DATA_DISABLED;
             sendMessage(msg);
-            return true;
-        } else {
-            // isEnabled && enable
-            return true;
         }
     }
 
@@ -536,7 +528,7 @@
      * {@code true} otherwise.
      */
     public boolean getAnyDataEnabled() {
-        return dataEnabled[APN_DEFAULT_ID] || dataEnabled[APN_MMS_ID] || dataEnabled[APN_SUPL_ID];
+        return (enabledCount != 0);
     }
 
     /**
@@ -618,7 +610,7 @@
             }
 
             if (DBG) {
-                log ("Setup watingApns : " + apnListToString(waitingApns));
+                log ("Setup waitngApns : " + apnListToString(waitingApns));
             }
             return setupData(reason);
         } else {
@@ -629,6 +621,7 @@
                     " sim=" + mGsmPhone.mSIMRecords.getRecordsLoaded() +
                     " UMTS=" + mGsmPhone.mSST.isConcurrentVoiceAndData() +
                     " phoneState=" + phone.getState() +
+                    " isDataAllowed=" + isDataAllowed() +
                     " dataEnabled=" + getAnyDataEnabled() +
                     " roaming=" + roaming +
                     " dataOnRoamingEnable=" + getDataOnRoamingEnabled() +
@@ -1251,15 +1244,6 @@
         trySetupData(reason);
     }
 
-    protected void onRestoreDefaultApn() {
-        if (DBG) Log.d(LOG_TAG, "Restore default APN");
-        setEnabled(Phone.APN_TYPE_MMS, false);
-        mRequestedApnType = Phone.APN_TYPE_DEFAULT;
-        if (!isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
-            cleanUpConnection(true, Phone.REASON_RESTORE_DEFAULT_APN);
-        }
-    }
-
     protected void onRoamingOff() {
         trySetupData(Phone.REASON_ROAMING_OFF);
     }
@@ -1313,22 +1297,6 @@
 
         if (ar.exception == null) {
             // everything is setup
-
-            /*
-             * We may have switched away from the default PDP context
-             * in order to enable a "special" APN (e.g., for MMS
-             * traffic). Set a timer to switch back and/or disable the
-             * special APN, so that a negligient application doesn't
-             * permanently prevent data connectivity. What we are
-             * protecting against here is not malicious apps, but
-             * rather an app that inadvertantly fails to reset to the
-             * default APN, or that dies before doing so.
-             */
-            if (dataEnabled[APN_MMS_ID] || dataEnabled[APN_SUPL_ID]) {
-                removeMessages(EVENT_RESTORE_DEFAULT_APN);
-                sendMessageDelayed(obtainMessage(EVENT_RESTORE_DEFAULT_APN),
-                        getRestoreDefaultApnDelay());
-            }
             if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
                 SystemProperties.set("gsm.defaultpdpcontext.active", "true");
                         if (canSetPreferApn && preferredApn == null) {
@@ -1432,18 +1400,6 @@
         cleanUpConnection(tearDown, reason);
     }
 
-    private int getRestoreDefaultApnDelay() {
-        String restoreApnDelayStr = SystemProperties.get(APN_RESTORE_DELAY_PROP_NAME);
-
-        if (restoreApnDelayStr != null && restoreApnDelayStr.length() != 0) {
-            try {
-                return Integer.valueOf(restoreApnDelayStr);
-            } catch (NumberFormatException e) {
-            }
-        }
-        return RESTORE_DEFAULT_APN_DELAY;
-   }
-
     /**
      * Based on the sim operator numeric, create a list for all possible pdps
      * with all apns associated with that pdp
@@ -1568,10 +1524,7 @@
 
     private void startDelayedRetry(PdpConnection.FailCause cause, String reason) {
         notifyNoData(cause);
-        if (mRequestedApnType != Phone.APN_TYPE_DEFAULT) {
-            sendMessage(obtainMessage(EVENT_RESTORE_DEFAULT_APN));
-        }
-        else {
+        if (mRequestedApnType == Phone.APN_TYPE_DEFAULT) {
             reconnectAfterFail(cause, reason);
         }
     }
@@ -1626,7 +1579,7 @@
     }
 
     public void handleMessage (Message msg) {
-
+        if (DBG) Log.d(LOG_TAG,"GSMDataConnTrack handleMessage "+msg);
         switch (msg.what) {
             case EVENT_RECORDS_LOADED:
                 onRecordsLoaded();
@@ -1636,10 +1589,6 @@
                 onEnableNewApn();
                 break;
 
-            case EVENT_RESTORE_DEFAULT_APN:
-                onRestoreDefaultApn();
-                break;
-
             case EVENT_GPRS_DETACHED:
                 onGprsDetached();
                 break;