Make NetworkStateTracker an interface

As part of the WifiStateTracker cleanup, need
to have NetworkStateTracker as an interface.

Change-Id: I2ac48d7e4c7274ac4df40bc3b8591b182956a936
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index ce6d613..5fd5315 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -22,11 +22,13 @@
 import android.content.IntentFilter;
 import android.os.RemoteException;
 import android.os.Handler;
+import android.os.Message;
 import android.os.ServiceManager;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyIntents;
 import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkInfo;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 import android.text.TextUtils;
@@ -38,7 +40,7 @@
  *
  * {@hide}
  */
-public class MobileDataStateTracker extends NetworkStateTracker {
+public class MobileDataStateTracker implements NetworkStateTracker {
 
     private static final String TAG = "MobileDataStateTracker";
     private static final boolean DBG = true;
@@ -48,6 +50,15 @@
 
     private String mApnType;
     private BroadcastReceiver mStateReceiver;
+    private static String[] sDnsPropNames;
+    private NetworkInfo mNetworkInfo;
+    private boolean mTeardownRequested = false;
+    private Handler mTarget;
+    private Context mContext;
+    private String mInterfaceName;
+    private boolean mPrivateDnsRouteSet = false;
+    private int mDefaultGatewayAddr = 0;
+    private boolean mDefaultRouteSet = false;
 
     /**
      * Create a new MobileDataStateTracker
@@ -58,14 +69,16 @@
      * @param tag the name of this network
      */
     public MobileDataStateTracker(Context context, Handler target, int netType, String tag) {
-        super(context, target, netType,
+        mTarget = target;
+        mContext = context;
+        mNetworkInfo = new NetworkInfo(netType,
                 TelephonyManager.getDefault().getNetworkType(), tag,
                 TelephonyManager.getDefault().getNetworkTypeName());
         mApnType = networkTypeToApnType(netType);
 
         mPhoneService = null;
 
-        mDnsPropNames = new String[] {
+        sDnsPropNames = new String[] {
                 "net.rmnet0.dns1",
                 "net.rmnet0.dns2",
                 "net.eth0.dns1",
@@ -80,6 +93,53 @@
     }
 
     /**
+     * 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[] getDnsPropNames() {
+        return sDnsPropNames;
+    }
+
+    /**
+     * Return the name of our network interface.
+     * @return the name of our interface.
+     */
+    public String getInterfaceName() {
+        return mInterfaceName;
+    }
+
+    public boolean isPrivateDnsRouteSet() {
+        return mPrivateDnsRouteSet;
+    }
+
+    public void privateDnsRouteSet(boolean enabled) {
+        mPrivateDnsRouteSet = enabled;
+    }
+
+    public NetworkInfo getNetworkInfo() {
+        return mNetworkInfo;
+    }
+
+    public int getDefaultGatewayAddr() {
+        return mDefaultGatewayAddr;
+    }
+
+    public boolean isDefaultRouteSet() {
+        return mDefaultRouteSet;
+    }
+
+    public void defaultRouteSet(boolean enabled) {
+        mDefaultRouteSet = enabled;
+    }
+
+    /**
+     * This is not implemented.
+     */
+    public void releaseWakeLock() {
+    }
+
+    /**
      * Begin monitoring mobile data connectivity.
      */
     public void startMonitoring() {
@@ -93,6 +153,32 @@
         mMobileDataState = Phone.DataState.DISCONNECTED;
     }
 
+    /**
+     * Record the roaming status of the device, and if it is a change from the previous
+     * status, send a notification to any listeners.
+     * @param isRoaming {@code true} if the device is now roaming, {@code false}
+     * if it is no longer roaming.
+     */
+    private void setRoamingStatus(boolean isRoaming) {
+        if (isRoaming != mNetworkInfo.isRoaming()) {
+            mNetworkInfo.setRoaming(isRoaming);
+            Message msg = mTarget.obtainMessage(EVENT_ROAMING_CHANGED, mNetworkInfo);
+            msg.sendToTarget();
+        }
+    }
+
+    private void setSubtype(int subtype, String subtypeName) {
+        if (mNetworkInfo.isConnected()) {
+            int oldSubtype = mNetworkInfo.getSubtype();
+            if (subtype != oldSubtype) {
+                mNetworkInfo.setSubtype(subtype, subtypeName);
+                Message msg = mTarget.obtainMessage(
+                        EVENT_NETWORK_SUBTYPE_CHANGED, oldSubtype, 0, mNetworkInfo);
+                msg.sendToTarget();
+            }
+        }
+    }
+
     private class MobileDataStateReceiver extends BroadcastReceiver {
         public void onReceive(Context context, Intent intent) {
             synchronized(this) {
@@ -260,13 +346,63 @@
      * mobile data connections.
      * TODO - make async and return nothing?
      */
-    @Override
     public boolean teardown() {
         setTeardownRequested(true);
         return (setEnableApn(mApnType, false) != Phone.APN_REQUEST_FAILED);
     }
 
     /**
+     * Record the detailed state of a network, and if it is a
+     * change from the previous state, send a notification to
+     * any listeners.
+     * @param state the new @{code DetailedState}
+     */
+    private void setDetailedState(NetworkInfo.DetailedState state) {
+        setDetailedState(state, null, null);
+    }
+
+    /**
+     * Record the detailed state of a network, and if it is a
+     * change from the previous state, send a notification to
+     * any listeners.
+     * @param state the new @{code DetailedState}
+     * @param reason a {@code String} indicating a reason for the state change,
+     * if one was supplied. May be {@code null}.
+     * @param extraInfo optional {@code String} providing extra information about the state change
+     */
+    private 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();
+            /*
+             * If a reason was supplied when the CONNECTING state was entered, and no
+             * reason was supplied for entering the CONNECTED state, then retain the
+             * reason that was supplied when going to CONNECTING.
+             */
+            if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null
+                    && lastReason != null)
+                reason = lastReason;
+            mNetworkInfo.setDetailedState(state, reason, extraInfo);
+            Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
+            msg.sendToTarget();
+        }
+    }
+
+    private void setDetailedStateInternal(NetworkInfo.DetailedState state) {
+        mNetworkInfo.setDetailedState(state, null, null);
+    }
+
+    public void setTeardownRequested(boolean isRequested) {
+        mTeardownRequested = isRequested;
+    }
+
+    public boolean isTeardownRequested() {
+        return mTeardownRequested;
+    }
+
+    /**
      * Re-enable mobile data connectivity after a {@link #teardown()}.
      * TODO - make async and always get a notification?
      */
@@ -322,23 +458,50 @@
     }
 
     /**
-     * Ensure that a network route exists to deliver traffic to the specified
-     * host via the mobile data network.
-     * @param hostAddress the IP address of the host to which the route is desired,
-     * in network byte order.
-     * @return {@code true} on success, {@code false} on failure
+     * Tells the phone sub-system that the caller wants to
+     * begin using the named feature. The only supported features at
+     * this time are {@code Phone.FEATURE_ENABLE_MMS}, which allows an application
+     * to specify that it wants to send and/or receive MMS data, and
+     * {@code Phone.FEATURE_ENABLE_SUPL}, which is used for Assisted GPS.
+     * @param feature the name of the feature to be used
+     * @param callingPid the process ID of the process that is issuing this request
+     * @param callingUid the user ID of the process that is issuing this request
+     * @return an integer value representing the outcome of the request.
+     * The interpretation of this value is feature-specific.
+     * specific, except that the value {@code -1}
+     * always indicates failure. For {@code Phone.FEATURE_ENABLE_MMS},
+     * the other possible return values are
+     * <ul>
+     * <li>{@code Phone.APN_ALREADY_ACTIVE}</li>
+     * <li>{@code Phone.APN_REQUEST_STARTED}</li>
+     * <li>{@code Phone.APN_TYPE_NOT_AVAILABLE}</li>
+     * <li>{@code Phone.APN_REQUEST_FAILED}</li>
+     * </ul>
      */
-    @Override
-    public boolean requestRouteToHost(int hostAddress) {
-        if (DBG) {
-            Log.d(TAG, "Requested host route to " + Integer.toHexString(hostAddress) +
-                    " for " + mApnType + "(" + mInterfaceName + ")");
-        }
-        if (mInterfaceName != null && hostAddress != -1) {
-            return NetworkUtils.addHostRoute(mInterfaceName, hostAddress) == 0;
-        } else {
-            return false;
-        }
+    public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
+        return -1;
+    }
+
+    /**
+     * Tells the phone sub-system that the caller is finished
+     * using the named feature. The only supported feature at
+     * this time is {@code Phone.FEATURE_ENABLE_MMS}, which allows an application
+     * to specify that it wants to send and/or receive MMS data.
+     * @param feature the name of the feature that is no longer needed
+     * @param callingPid the process ID of the process that is issuing this request
+     * @param callingUid the user ID of the process that is issuing this request
+     * @return an integer value representing the outcome of the request.
+     * The interpretation of this value is feature-specific, except that
+     * the value {@code -1} always indicates failure.
+     */
+    public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
+        return -1;
+    }
+
+    /**
+     * This is not supported.
+     */
+    public void interpretScanResultsAvailable() {
     }
 
     @Override
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 649cb8c..21f711c 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -121,7 +121,10 @@
      */
     public NetworkInfo(int type) {}
 
-    NetworkInfo(int type, int subtype, String typeName, String subtypeName) {
+    /**
+     * @hide
+     */
+    public NetworkInfo(int type, int subtype, String typeName, String subtypeName) {
         if (!ConnectivityManager.isNetworkTypeValid(type)) {
             throw new IllegalArgumentException("Invalid network type: " + type);
         }
@@ -281,8 +284,9 @@
      * 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) {
+    public void setDetailedState(DetailedState detailedState, String reason, String extraInfo) {
         this.mDetailedState = detailedState;
         this.mState = stateMap.get(detailedState);
         this.mReason = reason;
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 2992646..d5bcfcd3d 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -16,40 +16,15 @@
 
 package android.net;
 
-import java.io.FileWriter;
-import java.io.IOException;
-
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemProperties;
-import android.content.Context;
-import android.text.TextUtils;
-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
- * be kept in a Tracker class. This superclass manages the
- * network-type-independent aspects of network state.
+ * Interface for connectivity service to act on a network interface.
+ * All state information for a network should be kept in a Tracker class.
+ * This interface defines network-type-independent functions that should
+ * be implemented by the Tracker class.
  *
  * {@hide}
  */
-public abstract class NetworkStateTracker extends Handler {
-
-    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 = true;
-    private static final String TAG = "NetworkStateTracker";
+public interface NetworkStateTracker {
 
     public static final int EVENT_STATE_CHANGED = 1;
     public static final int EVENT_SCAN_RESULTS_AVAILABLE = 2;
@@ -64,322 +39,128 @@
     public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 6;
     public static final int EVENT_RESTORE_DEFAULT_NETWORK = 7;
 
-    public NetworkStateTracker(Context context,
-            Handler target,
-            int networkType,
-            int subType,
-            String typeName,
-            String subtypeName) {
-        super();
-        mContext = context;
-        mTarget = target;
-        mTeardownRequested = false;
-
-        this.mNetworkInfo = new NetworkInfo(networkType, subType, typeName, subtypeName);
-    }
-
-    public NetworkInfo getNetworkInfo() {
-        return mNetworkInfo;
-    }
+    /**
+     * Fetch NetworkInfo for the network
+     */
+    public NetworkInfo getNetworkInfo();
 
     /**
      * Return the system properties name associated with the tcp buffer sizes
      * for this network.
      */
-    public abstract String getTcpBufferSizesPropName();
+    public 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.
+     * Return the DNS property names for this network.
      */
-    public String[] getNameServers() {
-        return getNameServerList(mDnsPropNames);
-    }
+    public String[] getDnsPropNames();
 
     /**
-     * Return the IP addresses of the DNS servers available for this
-     * network interface.
-     * @param propertyNames the names of the system properties whose values
-     * give the IP addresses. Properties with no values are skipped.
-     * @return an array of {@code String}s containing the IP addresses
-     * of the DNS servers, in dot-notation. This may have fewer
-     * non-null entries than the list of names passed in, since
-     * some of the passed-in names may have empty values.
+     * Fetch interface name of the interface
      */
-    static protected String[] getNameServerList(String[] propertyNames) {
-        String[] dnsAddresses = new String[propertyNames.length];
-        int i, j;
-
-        for (i = 0, j = 0; i < propertyNames.length; i++) {
-            String value = SystemProperties.get(propertyNames[i]);
-            // The GSM layer sometimes sets a bogus DNS server address of
-            // 0.0.0.0
-            if (!TextUtils.isEmpty(value) && !TextUtils.equals(value, "0.0.0.0")) {
-                dnsAddresses[j++] = value;
-            }
-        }
-        return dnsAddresses;
-    }
-
-    public void addPrivateDnsRoutes() {
-        if (DBG) {
-            Log.d(TAG, "addPrivateDnsRoutes for " + this +
-                    "(" + mInterfaceName + ") - mPrivateDnsRouteSet = "+mPrivateDnsRouteSet);
-        }
-        if (mInterfaceName != null && !mPrivateDnsRouteSet) {
-            for (String addrString : getNameServers()) {
-                int addr = NetworkUtils.lookupHost(addrString);
-                if (addr != -1 && addr != 0) {
-                    if (DBG) Log.d(TAG, "  adding "+addrString+" ("+addr+")");
-                    NetworkUtils.addHostRoute(mInterfaceName, addr);
-                }
-            }
-            mPrivateDnsRouteSet = true;
-        }
-    }
-
-    public void removePrivateDnsRoutes() {
-        // 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) {
-            if (DBG) {
-                Log.d(TAG, "removePrivateDnsRoutes for " + mNetworkInfo.getTypeName() +
-                        " (" + mInterfaceName + ")");
-            }
-            NetworkUtils.removeHostRoutes(mInterfaceName);
-            mPrivateDnsRouteSet = false;
-        }
-    }
-
-    public void addDefaultRoute() {
-        if ((mInterfaceName != null) && (mDefaultGatewayAddr != 0) &&
-                mDefaultRouteSet == false) {
-            if (DBG) {
-                Log.d(TAG, "addDefaultRoute for " + mNetworkInfo.getTypeName() +
-                        " (" + mInterfaceName + "), GatewayAddr=" + mDefaultGatewayAddr);
-            }
-            NetworkUtils.setDefaultRoute(mInterfaceName, mDefaultGatewayAddr);
-            mDefaultRouteSet = true;
-        }
-    }
-
-    public void removeDefaultRoute() {
-        if (mInterfaceName != null && mDefaultRouteSet == true) {
-            if (DBG) {
-                Log.d(TAG, "removeDefaultRoute for " + mNetworkInfo.getTypeName() + " (" +
-                        mInterfaceName + ")");
-            }
-            NetworkUtils.removeDefaultRoute(mInterfaceName);
-            mDefaultRouteSet = false;
-        }
-    }
+    public String getInterfaceName();
 
     /**
-     * Reads the network specific TCP buffer sizes from SystemProperties
-     * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system
-     * wide use
+     * Check if private DNS route is set for the network
      */
-   public void updateNetworkSettings() {
-        String key = getTcpBufferSizesPropName();
-        String bufferSizes = SystemProperties.get(key);
+    public boolean isPrivateDnsRouteSet();
 
-        if (bufferSizes.length() == 0) {
-            Log.e(TAG, key + " not found in system properties. Using defaults");
+    /**
+     * Set a flag indicating private DNS route is set
+     */
+    public void privateDnsRouteSet(boolean enabled);
 
-            // Setting to default values so we won't be stuck to previous values
-            key = "net.tcp.buffersize.default";
-            bufferSizes = SystemProperties.get(key);
-        }
+    /**
+     * Fetch default gateway address for the network
+     */
+    public int getDefaultGatewayAddr();
 
-        // Set values in kernel
-        if (bufferSizes.length() != 0) {
-            if (DBG) {
-                Log.v(TAG, "Setting TCP values: [" + bufferSizes
-                        + "] which comes from [" + key + "]");
-            }
-            setBufferSize(bufferSizes);
-        }
-    }
+    /**
+     * Check if default route is set
+     */
+    public boolean isDefaultRouteSet();
+
+    /**
+     * Set a flag indicating default route is set for the network
+     */
+    public void defaultRouteSet(boolean enabled);
+
+    /**
+     * Indicate tear down requested from connectivity
+     */
+    public void setTeardownRequested(boolean isRequested);
+
+    /**
+     * Check if tear down was requested
+     */
+    public boolean isTeardownRequested();
 
     /**
      * Release the wakelock, if any, that may be held while handling a
      * disconnect operation.
      */
-    public void releaseWakeLock() {
-    }
+    public void releaseWakeLock();
 
-    /**
-     * Writes TCP buffer sizes to /sys/kernel/ipv4/tcp_[r/w]mem_[min/def/max]
-     * which maps to /proc/sys/net/ipv4/tcp_rmem and tcpwmem
-     * 
-     * @param bufferSizes in the format of "readMin, readInitial, readMax,
-     *        writeMin, writeInitial, writeMax"
-     */
-    private void setBufferSize(String bufferSizes) {
-        try {
-            String[] values = bufferSizes.split(",");
-
-            if (values.length == 6) {
-              final String prefix = "/sys/kernel/ipv4/tcp_";
-                stringToFile(prefix + "rmem_min", values[0]);
-                stringToFile(prefix + "rmem_def", values[1]);
-                stringToFile(prefix + "rmem_max", values[2]);
-                stringToFile(prefix + "wmem_min", values[3]);
-                stringToFile(prefix + "wmem_def", values[4]);
-                stringToFile(prefix + "wmem_max", values[5]);
-            } else {
-                Log.e(TAG, "Invalid buffersize string: " + bufferSizes);
-            }
-        } catch (IOException e) {
-            Log.e(TAG, "Can't set tcp buffer sizes:" + e);
-        }
-    }
-
-    /**
-     * Writes string to file. Basically same as "echo -n $string > $filename"
-     * 
-     * @param filename
-     * @param string
-     * @throws IOException
-     */
-    private void stringToFile(String filename, String string) throws IOException {
-        FileWriter out = new FileWriter(filename);
-        try {
-            out.write(string);
-        } finally {
-            out.close();
-        }
-    }
-
-    /**
-     * Record the detailed state of a network, and if it is a
-     * change from the previous state, send a notification to
-     * any listeners.
-     * @param state the new @{code DetailedState}
-     */
-    public void setDetailedState(NetworkInfo.DetailedState state) {
-        setDetailedState(state, null, null);
-    }
-
-    /**
-     * Record the detailed state of a network, and if it is a
-     * change from the previous state, send a notification to
-     * any listeners.
-     * @param state the new @{code DetailedState}
-     * @param reason a {@code String} indicating a reason for the state change,
-     * if one was supplied. May be {@code null}.
-     * @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();
-            /*
-             * If a reason was supplied when the CONNECTING state was entered, and no
-             * reason was supplied for entering the CONNECTED state, then retain the
-             * reason that was supplied when going to CONNECTING.
-             */
-            if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null
-                    && lastReason != null)
-                reason = lastReason;
-            mNetworkInfo.setDetailedState(state, reason, extraInfo);
-            Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
-            msg.sendToTarget();
-        }
-    }
-
-    protected void setDetailedStateInternal(NetworkInfo.DetailedState state) {
-        mNetworkInfo.setDetailedState(state, null, null);
-    }
-
-    public void setTeardownRequested(boolean isRequested) {
-        mTeardownRequested = isRequested;
-    }
-
-    public boolean isTeardownRequested() {
-        return mTeardownRequested;
-    }
-    
-    /**
-     * Send a  notification that the results of a scan for network access
-     * points has completed, and results are available.
-     */
-    protected void sendScanResultsAvailable() {
-        Message msg = mTarget.obtainMessage(EVENT_SCAN_RESULTS_AVAILABLE, mNetworkInfo);
-        msg.sendToTarget();
-    }
-
-    /**
-     * Record the roaming status of the device, and if it is a change from the previous
-     * status, send a notification to any listeners.
-     * @param isRoaming {@code true} if the device is now roaming, {@code false}
-     * if it is no longer roaming.
-     */
-    protected void setRoamingStatus(boolean isRoaming) {
-        if (isRoaming != mNetworkInfo.isRoaming()) {
-            mNetworkInfo.setRoaming(isRoaming);
-            Message msg = mTarget.obtainMessage(EVENT_ROAMING_CHANGED, mNetworkInfo);
-            msg.sendToTarget();
-        }
-    }
-
-    protected void setSubtype(int subtype, String subtypeName) {
-        if (mNetworkInfo.isConnected()) {
-            int oldSubtype = mNetworkInfo.getSubtype();
-            if (subtype != oldSubtype) {
-                mNetworkInfo.setSubtype(subtype, subtypeName);
-                Message msg = mTarget.obtainMessage(
-                        EVENT_NETWORK_SUBTYPE_CHANGED, oldSubtype, 0, mNetworkInfo);
-                msg.sendToTarget();
-            }
-        }
-    }
-
-    public abstract void startMonitoring();
+    public void startMonitoring();
 
     /**
      * Disable connectivity to a network
      * @return {@code true} if a teardown occurred, {@code false} if the
      * teardown did not occur.
      */
-    public abstract boolean teardown();
+    public boolean teardown();
 
     /**
      * Reenable connectivity to a network after a {@link #teardown()}.
      * @return {@code true} if we're connected or expect to be connected
      */
-    public abstract boolean reconnect();
+    public boolean reconnect();
 
     /**
      * Turn the wireless radio off for a network.
      * @param turnOn {@code true} to turn the radio on, {@code false}
      */
-    public abstract boolean setRadio(boolean turnOn);
+    public boolean setRadio(boolean turnOn);
 
     /**
      * Returns an indication of whether this network is available for
      * connections. A value of {@code false} means that some quasi-permanent
      * condition prevents connectivity to this network.
      */
-    public abstract boolean isAvailable();
+    public boolean isAvailable();
 
     /**
-     * Ensure that a network route exists to deliver traffic to the specified
-     * host via this network interface.
-     * @param hostAddress the IP address of the host to which the route is desired
-     * @return {@code true} on success, {@code false} on failure
+     * Tells the underlying networking system that the caller wants to
+     * begin using the named feature. The interpretation of {@code feature}
+     * is completely up to each networking implementation.
+     * @param feature the name of the feature to be used
+     * @param callingPid the process ID of the process that is issuing this request
+     * @param callingUid the user ID of the process that is issuing this request
+     * @return an integer value representing the outcome of the request.
+     * The interpretation of this value is specific to each networking
+     * implementation+feature combination, except that the value {@code -1}
+     * always indicates failure.
      */
-    public boolean requestRouteToHost(int hostAddress) {
-        return false;
-    }
+    public int startUsingNetworkFeature(String feature, int callingPid, int callingUid);
+
+    /**
+     * Tells the underlying networking system that the caller is finished
+     * using the named feature. The interpretation of {@code feature}
+     * is completely up to each networking implementation.
+     * @param feature the name of the feature that is no longer needed.
+     * @param callingPid the process ID of the process that is issuing this request
+     * @param callingUid the user ID of the process that is issuing this request
+     * @return an integer value representing the outcome of the request.
+     * The interpretation of this value is specific to each networking
+     * implementation+feature combination, except that the value {@code -1}
+     * always indicates failure.
+     */
+    public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid);
 
     /**
      * Interprets scan results. This will be called at a safe time for
      * processing, and from a safe thread.
      */
-    public void interpretScanResultsAvailable() {
-    }
+    public void interpretScanResultsAvailable();
 
 }
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 1ec8d0a..33df76c 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -28,6 +28,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkStateTracker;
 import android.net.wifi.WifiStateTracker;
+import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -46,6 +47,8 @@
 import com.android.server.connectivity.Tethering;
 
 import java.io.FileDescriptor;
+import java.io.FileWriter;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -743,7 +746,32 @@
             }
             return false;
         }
-        return tracker.requestRouteToHost(hostAddress);
+        return addHostRoute(tracker, hostAddress);
+    }
+
+    /**
+     * Ensure that a network route exists to deliver traffic to the specified
+     * host via the mobile data network.
+     * @param hostAddress the IP address of the host to which the route is desired,
+     * in network byte order.
+     * @return {@code true} on success, {@code false} on failure
+     */
+    private boolean addHostRoute(NetworkStateTracker nt, int hostAddress) {
+        if (nt.getNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI) {
+            return false;
+        }
+
+        String interfaceName = nt.getInterfaceName();
+
+        if (DBG) {
+            Slog.d(TAG, "Requested host route to " + Integer.toHexString(hostAddress) +
+                     "(" + interfaceName + ")");
+        }
+        if (interfaceName != null && hostAddress != -1) {
+            return NetworkUtils.addHostRoute(interfaceName, hostAddress) == 0;
+        } else {
+            return false;
+        }
     }
 
     /**
@@ -1133,7 +1161,7 @@
             mActiveDefaultNetwork = type;
         }
         thisNet.setTeardownRequested(false);
-        thisNet.updateNetworkSettings();
+        updateNetworkSettings(thisNet);
         handleConnectivityChange();
         sendConnectedBroadcast(info);
     }
@@ -1183,20 +1211,185 @@
         for (int netType : mPriorityList) {
             if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
                 if (mNetAttributes[netType].isDefault()) {
-                    mNetTrackers[netType].addDefaultRoute();
+                    addDefaultRoute(mNetTrackers[netType]);
                 } else {
-                    mNetTrackers[netType].addPrivateDnsRoutes();
+                    addPrivateDnsRoutes(mNetTrackers[netType]);
                 }
             } else {
                 if (mNetAttributes[netType].isDefault()) {
-                    mNetTrackers[netType].removeDefaultRoute();
+                    removeDefaultRoute(mNetTrackers[netType]);
                 } else {
-                    mNetTrackers[netType].removePrivateDnsRoutes();
+                    removePrivateDnsRoutes(mNetTrackers[netType]);
                 }
             }
         }
     }
 
+    private void addPrivateDnsRoutes(NetworkStateTracker nt) {
+        String interfaceName = nt.getInterfaceName();
+        boolean privateDnsRouteSet = nt.isPrivateDnsRouteSet();
+
+        if (DBG) {
+            Slog.d(TAG, "addPrivateDnsRoutes for " + nt +
+                    "(" + interfaceName + ") - mPrivateDnsRouteSet = " + privateDnsRouteSet);
+        }
+        String[] dnsList = getNameServerList(nt.getDnsPropNames());
+        if (interfaceName != null && !privateDnsRouteSet) {
+            for (String addrString : dnsList) {
+                int addr = NetworkUtils.lookupHost(addrString);
+                if (addr != -1 && addr != 0) {
+                    if (DBG) Slog.d(TAG, "  adding "+addrString+" ("+addr+")");
+                    NetworkUtils.addHostRoute(interfaceName, addr);
+                }
+            }
+            nt.privateDnsRouteSet(true);
+        }
+    }
+
+    private void removePrivateDnsRoutes(NetworkStateTracker nt) {
+        // TODO - we should do this explicitly but the NetUtils api doesnt
+        // support this yet - must remove all.  No worse than before
+        String interfaceName = nt.getInterfaceName();
+        boolean privateDnsRouteSet = nt.isPrivateDnsRouteSet();
+        if (interfaceName != null && privateDnsRouteSet) {
+            if (DBG) {
+                Slog.d(TAG, "removePrivateDnsRoutes for " + nt.getNetworkInfo().getTypeName() +
+                        " (" + interfaceName + ")");
+            }
+            NetworkUtils.removeHostRoutes(interfaceName);
+            nt.privateDnsRouteSet(false);
+        }
+    }
+
+    /**
+     * Return the IP addresses of the DNS servers available for this
+     * network interface.
+     * @param propertyNames the names of the system properties whose values
+     * give the IP addresses. Properties with no values are skipped.
+     * @return an array of {@code String}s containing the IP addresses
+     * of the DNS servers, in dot-notation. This may have fewer
+     * non-null entries than the list of names passed in, since
+     * some of the passed-in names may have empty values.
+     */
+    String[] getNameServerList(String[] propertyNames) {
+        String[] dnsAddresses = new String[propertyNames.length];
+        int i, j;
+
+        for (i = 0, j = 0; i < propertyNames.length; i++) {
+            String value = SystemProperties.get(propertyNames[i]);
+            // The GSM layer sometimes sets a bogus DNS server address of
+            // 0.0.0.0
+            if (!TextUtils.isEmpty(value) && !TextUtils.equals(value, "0.0.0.0")) {
+                dnsAddresses[j++] = value;
+            }
+        }
+        return dnsAddresses;
+    }
+
+    private void addDefaultRoute(NetworkStateTracker nt) {
+        String interfaceName = nt.getInterfaceName();
+        int defaultGatewayAddr = nt.getDefaultGatewayAddr();
+        boolean defaultRouteSet = nt.isDefaultRouteSet();
+        NetworkInfo networkInfo = nt.getNetworkInfo();
+
+        if ((interfaceName != null) && (defaultGatewayAddr != 0) &&
+                defaultRouteSet == false) {
+            if (DBG) {
+                Slog.d(TAG, "addDefaultRoute for " + networkInfo.getTypeName() +
+                        " (" + interfaceName + "), GatewayAddr=" + defaultGatewayAddr);
+            }
+            NetworkUtils.setDefaultRoute(interfaceName, defaultGatewayAddr);
+            nt.defaultRouteSet(true);
+        }
+    }
+
+
+    public void removeDefaultRoute(NetworkStateTracker nt) {
+        String interfaceName = nt.getInterfaceName();
+        boolean defaultRouteSet = nt.isDefaultRouteSet();
+        NetworkInfo networkInfo = nt.getNetworkInfo();
+
+        if (interfaceName != null && defaultRouteSet == true) {
+            if (DBG) {
+                Slog.d(TAG, "removeDefaultRoute for " + networkInfo.getTypeName() + " (" +
+                        interfaceName + ")");
+            }
+            NetworkUtils.removeDefaultRoute(interfaceName);
+            nt.defaultRouteSet(false);
+        }
+    }
+
+   /**
+     * Reads the network specific TCP buffer sizes from SystemProperties
+     * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system
+     * wide use
+     */
+   public void updateNetworkSettings(NetworkStateTracker nt) {
+        String key = nt.getTcpBufferSizesPropName();
+        String bufferSizes = SystemProperties.get(key);
+
+        if (bufferSizes.length() == 0) {
+            Slog.e(TAG, key + " not found in system properties. Using defaults");
+
+            // Setting to default values so we won't be stuck to previous values
+            key = "net.tcp.buffersize.default";
+            bufferSizes = SystemProperties.get(key);
+        }
+
+        // Set values in kernel
+        if (bufferSizes.length() != 0) {
+            if (DBG) {
+                Slog.v(TAG, "Setting TCP values: [" + bufferSizes
+                        + "] which comes from [" + key + "]");
+            }
+            setBufferSize(bufferSizes);
+        }
+    }
+
+   /**
+     * Writes TCP buffer sizes to /sys/kernel/ipv4/tcp_[r/w]mem_[min/def/max]
+     * which maps to /proc/sys/net/ipv4/tcp_rmem and tcpwmem
+     *
+     * @param bufferSizes in the format of "readMin, readInitial, readMax,
+     *        writeMin, writeInitial, writeMax"
+     */
+    private void setBufferSize(String bufferSizes) {
+        try {
+            String[] values = bufferSizes.split(",");
+
+            if (values.length == 6) {
+              final String prefix = "/sys/kernel/ipv4/tcp_";
+                stringToFile(prefix + "rmem_min", values[0]);
+                stringToFile(prefix + "rmem_def", values[1]);
+                stringToFile(prefix + "rmem_max", values[2]);
+                stringToFile(prefix + "wmem_min", values[3]);
+                stringToFile(prefix + "wmem_def", values[4]);
+                stringToFile(prefix + "wmem_max", values[5]);
+            } else {
+                Slog.e(TAG, "Invalid buffersize string: " + bufferSizes);
+            }
+        } catch (IOException e) {
+            Slog.e(TAG, "Can't set tcp buffer sizes:" + e);
+        }
+    }
+
+   /**
+     * Writes string to file. Basically same as "echo -n $string > $filename"
+     *
+     * @param filename
+     * @param string
+     * @throws IOException
+     */
+    private void stringToFile(String filename, String string) throws IOException {
+        FileWriter out = new FileWriter(filename);
+        try {
+            out.write(string);
+        } finally {
+            out.close();
+        }
+    }
+
+
     /**
      * Adjust the per-process dns entries (net.dns<x>.<pid>) based
      * on the highest priority active net which this process requested.
@@ -1216,7 +1409,7 @@
                 for (int j=0; j<pids.size(); j++) {
                     Integer pid = (Integer)pids.get(j);
                     if (pid.intValue() == myPid) {
-                        String[] dnsList = nt.getNameServers();
+                        String[] dnsList = getNameServerList(nt.getDnsPropNames());
                         writePidDns(dnsList, myPid);
                         if (doBump) {
                             bumpDns();
@@ -1270,7 +1463,7 @@
             NetworkStateTracker nt = mNetTrackers[netType];
             if (nt != null && nt.getNetworkInfo().isConnected() &&
                     !nt.isTeardownRequested()) {
-                String[] dnsList = nt.getNameServers();
+                String[] dnsList = getNameServerList(nt.getDnsPropNames());
                 if (mNetAttributes[netType].isDefault()) {
                     int j = 1;
                     for (String dns : dnsList) {
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 364cc16..76f6387 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -66,7 +66,7 @@
  *
  * @hide
  */
-public class WifiStateTracker extends NetworkStateTracker {
+public class WifiStateTracker extends Handler implements NetworkStateTracker {
 
     private static final boolean LOCAL_LOGD = Config.LOGD || false;
     
@@ -197,6 +197,8 @@
     private boolean mHaveIpAddress;
     private boolean mObtainingIpAddress;
     private boolean mTornDownByConnMgr;
+    private NetworkInfo mNetworkInfo;
+    private boolean mTeardownRequested = false;
     /**
      * A DISCONNECT event has been received, but processing it
      * is being deferred.
@@ -314,6 +316,11 @@
     private static String LS = System.getProperty("line.separator");
 
     private static String[] sDnsPropNames;
+    private Handler mTarget;
+    private Context mContext;
+    private boolean mPrivateDnsRouteSet = false;
+    private int mDefaultGatewayAddr = 0;
+    private boolean mDefaultRouteSet = false;
 
     /**
      * A structure for supplying information about a supplicant state
@@ -349,8 +356,9 @@
     }
 
     public WifiStateTracker(Context context, Handler target) {
-        super(context, target, ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
-        
+        mTarget = target;
+        mContext = context;
+        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
         mWifiInfo = new WifiInfo();
         mWifiMonitor = new WifiMonitor(this);
         mHaveIpAddress = false;
@@ -406,6 +414,57 @@
     }
 
     /**
+     * Record the detailed state of a network, and if it is a
+     * change from the previous state, send a notification to
+     * any listeners.
+     * @param state the new @{code DetailedState}
+     */
+    private void setDetailedState(NetworkInfo.DetailedState state) {
+        setDetailedState(state, null, null);
+    }
+
+    /**
+     * Record the detailed state of a network, and if it is a
+     * change from the previous state, send a notification to
+     * any listeners.
+     * @param state the new @{code DetailedState}
+     * @param reason a {@code String} indicating a reason for the state change,
+     * if one was supplied. May be {@code null}.
+     * @param extraInfo optional {@code String} providing extra information about the state change
+     */
+    private void setDetailedState(NetworkInfo.DetailedState state, String reason, String extraInfo) {
+        if (LOCAL_LOGD) 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();
+            /*
+             * If a reason was supplied when the CONNECTING state was entered, and no
+             * reason was supplied for entering the CONNECTED state, then retain the
+             * reason that was supplied when going to CONNECTING.
+             */
+            if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null
+                    && lastReason != null)
+                reason = lastReason;
+            mNetworkInfo.setDetailedState(state, reason, extraInfo);
+            Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
+            msg.sendToTarget();
+        }
+    }
+
+    private void setDetailedStateInternal(NetworkInfo.DetailedState state) {
+        mNetworkInfo.setDetailedState(state, null, null);
+    }
+
+    public void setTeardownRequested(boolean isRequested) {
+        mTeardownRequested = isRequested;
+    }
+
+    public boolean isTeardownRequested() {
+        return mTeardownRequested;
+    }
+
+    /**
      * Helper method: sets the boolean indicating that the connection
      * manager asked the network to be torn down (and so only the connection
      * manager can set it up again).
@@ -422,8 +481,8 @@
      * network interface.
      * @return a list of DNS addresses, with no holes.
      */
-    public String[] getNameServers() {
-        return getNameServerList(sDnsPropNames);
+    public String[] getDnsPropNames() {
+        return sDnsPropNames;
     }
 
     /**
@@ -434,6 +493,30 @@
         return mInterfaceName;
     }
 
+    public boolean isPrivateDnsRouteSet() {
+        return mPrivateDnsRouteSet;
+    }
+
+    public void privateDnsRouteSet(boolean enabled) {
+        mPrivateDnsRouteSet = enabled;
+    }
+
+    public NetworkInfo getNetworkInfo() {
+        return mNetworkInfo;
+    }
+
+    public int getDefaultGatewayAddr() {
+        return mDefaultGatewayAddr;
+    }
+
+    public boolean isDefaultRouteSet() {
+        return mDefaultRouteSet;
+    }
+
+    public void defaultRouteSet(boolean enabled) {
+        mDefaultRouteSet = enabled;
+    }
+
     /**
      * Return the system properties name associated with the tcp buffer sizes
      * for this network.
@@ -692,7 +775,6 @@
      * messages in the asynchronous call
      * from ConnectivityService
      */
-    @Override
     public void releaseWakeLock() {
     }
 
@@ -2051,7 +2133,26 @@
         return mWM.setWifiEnabled(turnOn);
     }
 
-    @Override
+    /**
+     * {@inheritDoc}
+     * There are currently no Wi-Fi-specific features supported.
+     * @param feature the name of the feature
+     * @return {@code -1} indicating failure, always
+     */
+    public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
+        return -1;
+    }
+
+    /**
+     * {@inheritDoc}
+     * There are currently no Wi-Fi-specific features supported.
+     * @param feature the name of the feature
+     * @return {@code -1} indicating failure, always
+     */
+    public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
+        return -1;
+    }
+
     public void interpretScanResultsAvailable() {
 
         // If we shouldn't place a notification on available networks, then
@@ -2097,6 +2198,15 @@
     }
 
     /**
+     * Send a  notification that the results of a scan for network access
+     * points has completed, and results are available.
+     */
+    private void sendScanResultsAvailable() {
+        Message msg = mTarget.obtainMessage(EVENT_SCAN_RESULTS_AVAILABLE, mNetworkInfo);
+        msg.sendToTarget();
+    }
+
+    /**
      * Display or don't display a notification that there are open Wi-Fi networks.
      * @param visible {@code true} if notification should be visible, {@code false} otherwise
      * @param numNetworks the number networks seen