Code drop from //branches/cupcake/...@124589
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index f776abf..8c82212 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -99,24 +99,38 @@
     }
     
     private int mNetworkType;
+    private int mSubtype;
+    private String mTypeName;
+    private String mSubtypeName;
     private State mState;
     private DetailedState mDetailedState;
     private String mReason;
     private String mExtraInfo;
     private boolean mIsFailover;
+    private boolean mIsRoaming;
     /**
      * Indicates whether network connectivity is possible:
      */
     private boolean mIsAvailable;
 
-    public NetworkInfo(int type) {
+    /**
+     * TODO This is going away as soon as API council review happens.
+     * @param type network type
+     */
+    public NetworkInfo(int type) {}
+
+    NetworkInfo(int type, int subtype, String typeName, String subtypeName) {
         if (!ConnectivityManager.isNetworkTypeValid(type)) {
             throw new IllegalArgumentException("Invalid network type: " + type);
         }
-        this.mNetworkType = type;
+        mNetworkType = type;
+        mSubtype = subtype;
+        mTypeName = typeName;
+        mSubtypeName = subtypeName;
         setDetailedState(DetailedState.IDLE, null, null);
         mState = State.UNKNOWN;
         mIsAvailable = true;
+        mIsRoaming = false;
     }
 
     /**
@@ -129,6 +143,41 @@
     }
 
     /**
+     * Return a network-type-specific integer describing the subtype
+     * of the network.
+     * @return the network subtype
+     *
+     * @hide pending API council review
+     */
+    public int getSubtype() {
+        return mSubtype;
+    }
+
+    void setSubtype(int subtype, String subtypeName) {
+        mSubtype = subtype;
+        mSubtypeName = subtypeName;
+    }
+
+    /**
+     * Return a human-readable name describe the type of the network,
+     * for example "WIFI" or "MOBILE".
+     * @return the name of the network type
+     */
+    public String getTypeName() {
+        return mTypeName;
+    }
+
+    /**
+     * Return a human-readable name describing the subtype of the network.
+     * @return the name of the network subtype
+     * 
+     * @hide pending API council review
+     */
+    public String getSubtypeName() {
+        return mSubtypeName;
+    }
+
+    /**
      * Indicates whether network connectivity exists or is in the process
      * of being established. This is good for applications that need to
      * do anything related to the network other than read or write data.
@@ -170,7 +219,7 @@
      * Sets if the network is available, ie, if the connectivity is possible.
      * @param isAvailable the new availability value.
      *
-     * {@hide}
+     * @hide
      */
     public void setIsAvailable(boolean isAvailable) {
         mIsAvailable = isAvailable;
@@ -187,12 +236,33 @@
         return mIsFailover;
     }
 
-    /** {@hide} */
+    /**
+     * Set the failover boolean.
+     * @param isFailover {@code true} to mark the current connection attempt
+     * as a failover.
+     * @hide
+     */
     public void setFailover(boolean isFailover) {
         mIsFailover = isFailover;
     }
 
     /**
+     * Indicates whether the device is currently roaming on this network.
+     * When {@code true}, it suggests that use of data on this network
+     * may incur extra costs.
+     * @return {@code true} if roaming is in effect, {@code false} otherwise.
+     *
+     * @hide pending API council
+     */
+    public boolean isRoaming() {
+        return mIsRoaming;
+    }
+
+    void setRoaming(boolean isRoaming) {
+        mIsRoaming = isRoaming;
+    }
+
+    /**
      * Reports the current coarse-grained state of the network.
      * @return the coarse-grained state
      */
@@ -215,8 +285,6 @@
      * 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;
@@ -247,52 +315,59 @@
     @Override
     public String toString() {
         StringBuilder builder = new StringBuilder("NetworkInfo: ");
-        builder.append("type: ").append(getTypeName()).append(", state: ").append(mState).
-                append("/").append(mDetailedState).
+        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();
     }
 
-    public String getTypeName() {
-        switch (mNetworkType) {
-            case ConnectivityManager.TYPE_WIFI:
-                return "WIFI";
-            case ConnectivityManager.TYPE_MOBILE:
-                return "MOBILE";
-            default:
-                return "<invalid>";
-        }
-    }
-
-    /** Implement the Parcelable interface {@hide} */
+    /**
+     * Implement the Parcelable interface
+     * @hide
+     */
     public int describeContents() {
         return 0;
     }
 
-    /** Implement the Parcelable interface {@hide} */
+    /**
+     * Implement the Parcelable interface.
+     * @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);
     }
 
-    /** Implement the Parcelable interface {@hide} */
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
     public static final Creator<NetworkInfo> CREATOR =
         new Creator<NetworkInfo>() {
             public NetworkInfo createFromParcel(Parcel in) {
                 int netType = in.readInt();
-                NetworkInfo netInfo = new NetworkInfo(netType);
+                int subtype = in.readInt();
+                String typeName = in.readString();
+                String subtypeName = in.readString();
+                NetworkInfo netInfo = new NetworkInfo(netType, subtype, typeName, subtypeName);
                 netInfo.mState = State.valueOf(in.readString());
                 netInfo.mDetailedState = DetailedState.valueOf(in.readString());
                 netInfo.mIsFailover = in.readInt() != 0;
                 netInfo.mIsAvailable = in.readInt() != 0;
+                netInfo.mIsRoaming = in.readInt() != 0;
                 netInfo.mReason = in.readString();
                 netInfo.mExtraInfo = in.readString();
                 return netInfo;
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 129248a..1153648 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -67,6 +67,14 @@
     public native static boolean stopDhcp(String interfaceName);
 
     /**
+     * Release the current DHCP lease.
+     * @param interfaceName the name of the interface for which the lease should
+     * be released
+     * @return {@code true} for success, {@code false} for failure
+     */
+    public native static boolean releaseDhcpLease(String interfaceName);
+
+    /**
      * Return the last DHCP-related error message that was recorded.
      * <p/>NOTE: This string is not localized, but currently it is only
      * used in logging.
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 417ce54..8383deb 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -41,6 +41,7 @@
                     in_addr_t *server,
                     uint32_t  *lease);
 int dhcp_stop(const char *ifname);
+int dhcp_release_lease(const char *ifname);
 char *dhcp_get_errmsg();
 }
 
@@ -157,7 +158,7 @@
     return (jboolean)(result == 0);
 }
 
-static jboolean android_net_utils_stopDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
+static jboolean android_net_utils_stopDhcp(JNIEnv* env, jobject clazz, jstring ifname)
 {
     int result;
 
@@ -167,6 +168,16 @@
     return (jboolean)(result == 0);
 }
 
+static jboolean android_net_utils_releaseDhcpLease(JNIEnv* env, jobject clazz, jstring ifname)
+{
+    int result;
+
+    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+    result = ::dhcp_release_lease(nameStr);
+    env->ReleaseStringUTFChars(ifname, nameStr);
+    return (jboolean)(result == 0);
+}
+
 static jstring android_net_utils_getDhcpError(JNIEnv* env, jobject clazz)
 {
     return env->NewStringUTF(::dhcp_get_errmsg());
@@ -207,6 +218,7 @@
     { "resetConnections", "(Ljava/lang/String;)I",  (void *)android_net_utils_resetConnections },
     { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z",  (void *)android_net_utils_runDhcp },
     { "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 },
 };
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 11f34cc..65e3650 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -35,7 +35,6 @@
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.provider.Settings;
-import android.telephony.TelephonyManager;
 import android.util.EventLog;
 import android.util.Log;
 
@@ -43,7 +42,7 @@
 import java.io.PrintWriter;
 
 /**
- * {@hide}
+ * @hide
  */
 public class ConnectivityService extends IConnectivityManager.Stub {
 
@@ -59,7 +58,6 @@
      * abstractly.
      */
     private NetworkStateTracker mNetTrackers[];
-    private boolean mTeardownRequested[];
     private WifiStateTracker mWifiStateTracker;
     private MobileDataStateTracker mMobileDataStateTracker;
     private WifiWatchdogService mWifiWatchdogService;
@@ -69,11 +67,11 @@
     private NetworkStateTracker mActiveNetwork;
 
     private int mNumDnsEntries;
-    private static int mDnsChangeCounter;
+    private static int sDnsChangeCounter;
 
     private boolean mTestMode;
     private static ConnectivityService sServiceInstance;
-    
+
     private static class ConnectivityThread extends Thread {
         private Context mContext;
         
@@ -101,7 +99,9 @@
                     try {
                         // Wait until sServiceInstance has been initialized.
                         thread.wait();
-                    } catch (InterruptedException e) {
+                    } catch (InterruptedException ignore) {
+                        Log.e(TAG,
+                            "Unexpected InterruptedException while waiting for ConnectivityService thread");
                     }
                 }
             }
@@ -118,7 +118,6 @@
         if (DBG) Log.v(TAG, "ConnectivityService starting up");
         mContext = context;
         mNetTrackers = new NetworkStateTracker[2];
-        mTeardownRequested = new boolean[2];
         Handler handler = new MyHandler();
         
         mNetworkPreference = getPersistedNetworkPreference();
@@ -134,7 +133,6 @@
         mWifiStateTracker = new WifiStateTracker(context, handler);
         WifiService wifiService = new WifiService(context, mWifiStateTracker);
         ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
-        // The WifiStateTracker should appear first in the list
         mNetTrackers[ConnectivityManager.TYPE_WIFI] = mWifiStateTracker;
 
         mMobileDataStateTracker = new MobileDataStateTracker(context, handler);
@@ -160,10 +158,11 @@
     public synchronized void setNetworkPreference(int preference) {
         enforceChangePermission();
         if (ConnectivityManager.isNetworkTypeValid(preference)) {
-            int oldPreference = mNetworkPreference;
-            persistNetworkPreference(preference);
-            if (mNetworkPreference != oldPreference)
+            if (mNetworkPreference != preference) {
+                persistNetworkPreference(preference);
+                mNetworkPreference = preference;
                 enforcePreference();
+            }
         }
     }
 
@@ -174,14 +173,14 @@
 
     private void persistNetworkPreference(int networkPreference) {
         final ContentResolver cr = mContext.getContentResolver();
-        Settings.System.putInt(cr, Settings.System.NETWORK_PREFERENCE, networkPreference);
+        Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, networkPreference);
     }
     
     private int getPersistedNetworkPreference() {
         final ContentResolver cr = mContext.getContentResolver();
 
-        final int networkPrefSetting = Settings.System
-                .getInt(cr, Settings.System.NETWORK_PREFERENCE, -1);
+        final int networkPrefSetting = Settings.Secure
+                .getInt(cr, Settings.Secure.NETWORK_PREFERENCE, -1);
         if (networkPrefSetting != -1) {
             return networkPrefSetting;
         }
@@ -219,7 +218,7 @@
 
     private boolean teardown(NetworkStateTracker netTracker) {
         if (netTracker.teardown()) {
-            mTeardownRequested[netTracker.getNetworkInfo().getType()] = true;
+            netTracker.setTeardownRequested(true);
             return true;
         } else {
             return false;
@@ -227,9 +226,9 @@
     }
 
     /**
-     * Return NetworkInfo for the active network interface. It is assumed that at most
-     * one network is active at a time. If more than one is active, it is indeterminate
-     * which will be returned.
+     * Return NetworkInfo for the active (i.e., connected) network interface.
+     * It is assumed that at most one network is active at a time. If more
+     * than one is active, it is indeterminate which will be returned.
      * @return the info for the active network, or {@code null} if none is active
      */
     public NetworkInfo getActiveNetworkInfo() {
@@ -291,7 +290,6 @@
             return tracker.startUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
         }
         return -1;
-
     }
 
     public int stopUsingNetworkFeature(int networkType, String feature) {
@@ -339,8 +337,7 @@
         int numConnectedNets = 0;
 
         for (NetworkStateTracker nt : mNetTrackers) {
-            if (nt.getNetworkInfo().isConnected()
-                    && !mTeardownRequested[nt.getNetworkInfo().getType()]) {
+            if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
                 ++numConnectedNets;
             }
         }
@@ -369,13 +366,13 @@
 
         if (DBG) Log.v(TAG, "Handle DISCONNECT for " + info.getTypeName());
 
+        mNetTrackers[info.getType()].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.
          */
-        mTeardownRequested[info.getType()] = false;
         if (mActiveNetwork == null ||  info.getType() != mActiveNetwork.getNetworkInfo().getType())
             return;
 
@@ -386,13 +383,23 @@
             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())
+            if (!switchTo.isConnectedOrConnecting()) {
                 newNet.reconnect();
+            }
+        } else {
+            newNet.reconnect();
         }
 
         boolean otherNetworkConnected = false;
@@ -449,8 +456,12 @@
         mContext.sendStickyBroadcast(intent);
     }
 
+    /**
+     * Called when an attempt to fail over to another network has failed.
+     * @param info the {@link NetworkInfo} for the failed network
+     */
     private void handleConnectionFailure(NetworkInfo info) {
-        mTeardownRequested[info.getType()] = false;
+        mNetTrackers[info.getType()].setTeardownRequested(false);
         if (getActiveNetworkInfo() == null) {
             String reason = info.getReason();
             String extraInfo = info.getExtraInfo();
@@ -496,10 +507,9 @@
             otherNet = mMobileDataStateTracker;
         }
         /*
-        * Check policy to see whether we are now connected to a network that
-        * takes precedence over the other one. If so, we need to tear down
-        * the other one.
-        */
+         * 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()) {
@@ -510,7 +520,7 @@
         }
 
         boolean toredown = false;
-        mTeardownRequested[info.getType()] = false;
+        thisNet.setTeardownRequested(false);
         if (!mTestMode && deadnet != null) {
             if (DBG) Log.v(TAG, "Policy requires " +
                   deadnet.getNetworkInfo().getTypeName() + " teardown");
@@ -520,6 +530,10 @@
             }
         }
 
+        /*
+         * 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());
@@ -592,10 +606,9 @@
         int incrValue = ConnectivityManager.TYPE_MOBILE - ConnectivityManager.TYPE_WIFI;
         int stopValue = ConnectivityManager.TYPE_MOBILE + incrValue;
 
-        for (int net = ConnectivityManager.TYPE_WIFI; net != stopValue; net += incrValue) {            
-            NetworkStateTracker nt = mNetTrackers[net];
-            if (nt.getNetworkInfo().isConnected()
-                    && !mTeardownRequested[nt.getNetworkInfo().getType()]) {
+        for (int netType = ConnectivityManager.TYPE_WIFI; netType != stopValue; netType += incrValue) {
+            NetworkStateTracker nt = mNetTrackers[netType];
+            if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
                 ++numConnectedNets;
                 String[] dnsList = nt.getNameServers();
                 for (int i = 0; i < dnsList.length && dnsList[i] != null; i++) {
@@ -613,7 +626,7 @@
         }
         mNumDnsEntries = index - 1;
         // Notify the name resolver library of the change
-        SystemProperties.set("net.dnschange", String.valueOf(mDnsChangeCounter++));
+        SystemProperties.set("net.dnschange", String.valueOf(sDnsChangeCounter++));
         return numConnectedNets;
     }
 
@@ -650,13 +663,13 @@
                             info.getState() + "/" + info.getDetailedState());
 
                     // Connectivity state changed:
-                    // [31-11] Reserved for future use
-                    // [10-9] Mobile network connection type (as defined by the TelephonyManager)
-                    // [8-3] Detailed state ordinal (as defined by NetworkInfo.DetailedState)   
+                    // [31-13] Reserved for future use
+                    // [12-9] Network subtype (for mobile network, as defined by TelephonyManager)
+                    // [8-3] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
                     // [2-0] Network type (as defined by ConnectivityManager)
                     int eventLogParam = (info.getType() & 0x7) |
-                         ((info.getDetailedState().ordinal() & 0x3f) << 3) |
-                         (TelephonyManager.getDefault().getNetworkType() << 9);
+                            ((info.getDetailedState().ordinal() & 0x3f) << 3) |
+                            (info.getSubtype() << 9);
                     EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED, eventLogParam);
                     
                     if (info.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
@@ -687,6 +700,14 @@
                 case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
                     handleConfigurationChange();
                     break;
+
+                case NetworkStateTracker.EVENT_ROAMING_CHANGED:
+                    // fill me in
+                    break;
+
+                case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
+                    // fill me in
+                    break;
             }
         }
     }