Initial Contribution
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
new file mode 100644
index 0000000..213813a
--- /dev/null
+++ b/core/java/android/net/ConnectivityManager.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.RemoteException;
+
+/**
+ * Class that answers queries about the state of network connectivity. It also
+ * notifies applications when network connectivity changes. Get an instance
+ * of this class by calling
+ * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context.CONNECTIVITY_SERVICE)}.
+ * <p>
+ * The primary responsibilities of this class are to:
+ * <ol>
+ * <li>Monitor network connections (Wi-Fi, GPRS, UMTS, etc.)</li>
+ * <li>Send broadcast intents when network connectivity changes</li>
+ * <li>Attempt to "fail over" to another network when connectivity to a network
+ * is lost</li>
+ * <li>Provide an API that allows applications to query the coarse-grained or fine-grained
+ * state of the available networks</li>
+ * </ol>
+ */
+public class ConnectivityManager
+{
+    /**
+     * A change in network connectivity has occurred. A connection has either
+     * been established or lost. The NetworkInfo for the affected network is
+     * sent as an extra; it should be consulted to see what kind of
+     * connectivity event occurred.
+     * <p/>
+     * If this is a connection that was the result of failing over from a
+     * disconnected network, then the FAILOVER_CONNECTION boolean extra is
+     * set to true.
+     * <p/>
+     * For a loss of connectivity, if the connectivity manager is attempting
+     * to connect (or has already connected) to another network, the
+     * NetworkInfo for the new network is also passed as an extra. This lets
+     * any receivers of the broadcast know that they should not necessarily
+     * tell the user that no data traffic will be possible. Instead, the
+     * reciever should expect another broadcast soon, indicating either that
+     * the failover attempt succeeded (and so there is still overall data
+     * connectivity), or that the failover attempt failed, meaning that all
+     * connectivity has been lost.
+     * <p/>
+     * For a disconnect event, the boolean extra EXTRA_NO_CONNECTIVITY
+     * is set to {@code true} if there are no connected networks at all.
+     */
+    public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
+    /**
+     * The lookup key for a {@link NetworkInfo} object. Retrieve with
+     * {@link android.content.Intent#getParcelableExtra(String)}.
+     */
+    public static final String EXTRA_NETWORK_INFO = "networkInfo";
+    /**
+     * The lookup key for a boolean that indicates whether a connect event
+     * is for a network to which the connectivity manager was failing over
+     * following a disconnect on another network.
+     * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}.
+     */
+    public static final String EXTRA_IS_FAILOVER = "isFailover";
+    /**
+     * The lookup key for a {@link NetworkInfo} object. This is supplied when
+     * there is another network that it may be possible to connect to. Retrieve with
+     * {@link android.content.Intent#getParcelableExtra(String)}.
+     */
+    public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
+    /**
+     * The lookup key for a boolean that indicates whether there is a
+     * complete lack of connectivity, i.e., no network is available.
+     * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}.
+     */
+    public static final String EXTRA_NO_CONNECTIVITY = "noConnectivity";
+    /**
+     * The lookup key for a string that indicates why an attempt to connect
+     * to a network failed. The string has no particular structure. It is
+     * intended to be used in notifications presented to users. Retrieve
+     * it with {@link android.content.Intent#getStringExtra(String)}.
+     */
+    public static final String EXTRA_REASON = "reason";
+    /**
+     * The lookup key for a string that provides optionally supplied
+     * extra information about the network state. The information
+     * may be passed up from the lower networking layers, and its
+     * meaning may be specific to a particular network type. Retrieve
+     * it with {@link android.content.Intent#getStringExtra(String)}.
+     */
+    public static final String EXTRA_EXTRA_INFO = "extraInfo";
+
+    public static final int TYPE_MOBILE = 0;
+    public static final int TYPE_WIFI   = 1;
+
+    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;
+    }
+
+    public void setNetworkPreference(int preference) {
+        try {
+            mService.setNetworkPreference(preference);
+        } catch (RemoteException e) {
+        }
+    }
+
+    public int getNetworkPreference() {
+        try {
+            return mService.getNetworkPreference();
+        } catch (RemoteException e) {
+            return -1;
+        }
+    }
+
+    public NetworkInfo getActiveNetworkInfo() {
+        try {
+            return mService.getActiveNetworkInfo();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    public NetworkInfo getNetworkInfo(int networkType) {
+        try {
+            return mService.getNetworkInfo(networkType);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    public NetworkInfo[] getAllNetworkInfo() {
+        try {
+            return mService.getAllNetworkInfo();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /** {@hide} */
+    public boolean setRadios(boolean turnOn) {
+        try {
+            return mService.setRadios(turnOn);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /** {@hide} */
+    public boolean setRadio(int networkType, boolean turnOn) {
+        try {
+            return mService.setRadio(networkType, turnOn);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * 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 networkType specifies which network the request pertains to
+     * @param feature the name of the feature to be used
+     * @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 startUsingNetworkFeature(int networkType, String feature) {
+        try {
+            return mService.startUsingNetworkFeature(networkType, feature);
+        } catch (RemoteException e) {
+            return -1;
+        }
+    }
+
+    /**
+     * 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 networkType specifies which network the request pertains to
+     * @param feature the name of the feature that is no longer needed
+     * @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(int networkType, String feature) {
+        try {
+            return mService.stopUsingNetworkFeature(networkType, feature);
+        } catch (RemoteException e) {
+            return -1;
+        }
+    }
+
+    /**
+     * Ensure that a network route exists to deliver traffic to the specified
+     * host via the specified network interface. An attempt to add a route that
+     * already exists is ignored, but treated as successful.
+     * @param networkType the type of the network over which traffic to the specified
+     * host is to be routed
+     * @param hostAddress the IP address of the host to which the route is desired
+     * @return {@code true} on success, {@code false} on failure
+     */
+    public boolean requestRouteToHost(int networkType, int hostAddress) {
+        try {
+            return mService.requestRouteToHost(networkType, hostAddress);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Don't allow use of default constructor.
+     */
+    @SuppressWarnings({"UnusedDeclaration"})
+    private ConnectivityManager() {
+    }
+
+    /**
+     * {@hide}
+     */
+    public ConnectivityManager(IConnectivityManager service) {
+        if (service == null) {
+            throw new IllegalArgumentException(
+                "ConnectivityManager() cannot be constructed with null service");
+        }
+        mService = service;
+    }
+}
diff --git a/core/java/android/net/DhcpInfo.java b/core/java/android/net/DhcpInfo.java
new file mode 100644
index 0000000..1178bec
--- /dev/null
+++ b/core/java/android/net/DhcpInfo.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * A simple object for retrieving the results of a DHCP request.
+ */
+public class DhcpInfo implements Parcelable {
+    public int ipAddress;
+    public int gateway;
+    public int netmask;
+
+    public int dns1;
+    public int dns2;
+
+    public int serverAddress;
+    public int leaseDuration;
+
+    public DhcpInfo() {
+        super();
+    }
+
+    public String toString() {
+        StringBuffer str = new StringBuffer();
+
+        str.append("ipaddr "); putAddress(str, ipAddress);
+        str.append(" gateway "); putAddress(str, gateway);
+        str.append(" netmask "); putAddress(str, netmask);
+        str.append(" dns1 "); putAddress(str, dns1);
+        str.append(" dns2 "); putAddress(str, dns2);
+        str.append(" DHCP server "); putAddress(str, serverAddress);
+        str.append(" lease ").append(leaseDuration).append(" seconds");
+
+        return str.toString();
+    }
+
+    private static void putAddress(StringBuffer buf, int addr) {
+        buf.append(addr  & 0xff).append('.').
+            append((addr >>>= 8) & 0xff).append('.').
+            append((addr >>>= 8) & 0xff).append('.').
+            append((addr >>>= 8) & 0xff);
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(ipAddress);
+        dest.writeInt(gateway);
+        dest.writeInt(netmask);
+        dest.writeInt(dns1);
+        dest.writeInt(dns2);
+        dest.writeInt(serverAddress);
+        dest.writeInt(leaseDuration);
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public static final Creator<DhcpInfo> CREATOR =
+        new Creator<DhcpInfo>() {
+            public DhcpInfo createFromParcel(Parcel in) {
+                DhcpInfo info = new DhcpInfo();
+                info.ipAddress = in.readInt();
+                info.gateway = in.readInt();
+                info.netmask = in.readInt();
+                info.dns1 = in.readInt();
+                info.dns2 = in.readInt();
+                info.serverAddress = in.readInt();
+                info.leaseDuration = in.readInt();
+                return info;
+            }
+
+            public DhcpInfo[] newArray(int size) {
+                return new DhcpInfo[size];
+            }
+        };
+}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
new file mode 100644
index 0000000..e1d921f
--- /dev/null
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.NetworkInfo;
+
+/**
+ * Interface that answers queries about, and allows changing, the
+ * state of network connectivity.
+ */
+/** {@hide} */
+interface IConnectivityManager
+{
+    void setNetworkPreference(int pref);
+
+    int getNetworkPreference();
+
+    NetworkInfo getActiveNetworkInfo();
+
+    NetworkInfo getNetworkInfo(int networkType);
+
+    NetworkInfo[] getAllNetworkInfo();
+
+    boolean setRadios(boolean onOff);
+
+    boolean setRadio(int networkType, boolean turnOn);
+
+    int startUsingNetworkFeature(int networkType, in String feature);
+
+    int stopUsingNetworkFeature(int networkType, in String feature);
+
+    boolean requestRouteToHost(int networkType, int hostAddress);
+}
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
new file mode 100644
index 0000000..f776abf
--- /dev/null
+++ b/core/java/android/net/NetworkInfo.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import java.util.EnumMap;
+
+/**
+ * Describes the status of a network interface of a given type
+ * (currently either Mobile or Wifi).
+ */
+public class NetworkInfo implements Parcelable {
+
+    /**
+     * Coarse-grained network state. This is probably what most applications should
+     * use, rather than {@link android.net.NetworkInfo.DetailedState DetailedState}.
+     * The mapping between the two is as follows:
+     * <br/><br/>
+     * <table>
+     * <tr><td><b>Detailed state</b></td><td><b>Coarse-grained state</b></td></tr>
+     * <tr><td><code>IDLE</code></td><td><code>DISCONNECTED</code></td></tr>
+     * <tr><td><code>SCANNING</code></td><td><code>CONNECTING</code></td></tr>
+     * <tr><td><code>CONNECTING</code></td><td><code>CONNECTING</code></td></tr>
+     * <tr><td><code>AUTHENTICATING</code></td><td><code>CONNECTING</code></td></tr>
+     * <tr><td><code>CONNECTED</code></td><td<code>CONNECTED</code></td></tr>
+     * <tr><td><code>DISCONNECTING</code></td><td><code>DISCONNECTING</code></td></tr>
+     * <tr><td><code>DISCONNECTED</code></td><td><code>DISCONNECTED</code></td></tr>
+     * <tr><td><code>UNAVAILABLE</code></td><td><code>DISCONNECTED</code></td></tr>
+     * <tr><td><code>FAILED</code></td><td><code>DISCONNECTED</code></td></tr>
+     * </table>
+     */
+    public enum State {
+        CONNECTING, CONNECTED, SUSPENDED, DISCONNECTING, DISCONNECTED, UNKNOWN
+    }
+
+    /**
+     * The fine-grained state of a network connection. This level of detail
+     * is probably of interest to few applications. Most should use
+     * {@link android.net.NetworkInfo.State State} instead.
+     */
+    public enum DetailedState {
+        /** Ready to start data connection setup. */
+        IDLE,
+        /** Searching for an available access point. */
+        SCANNING,
+        /** Currently setting up data connection. */
+        CONNECTING,
+        /** Network link established, performing authentication. */
+        AUTHENTICATING,
+        /** Awaiting response from DHCP server in order to assign IP address information. */
+        OBTAINING_IPADDR,
+        /** IP traffic should be available. */
+        CONNECTED,
+        /** IP traffic is suspended */
+        SUSPENDED,
+        /** Currently tearing down data connection. */
+        DISCONNECTING,
+        /** IP traffic not available. */
+        DISCONNECTED,
+        /** Attempt to connect failed. */
+        FAILED
+    }
+
+    /**
+     * This is the map described in the Javadoc comment above. The positions
+     * of the elements of the array must correspond to the ordinal values
+     * of <code>DetailedState</code>.
+     */
+    private static final EnumMap<DetailedState, State> stateMap =
+        new EnumMap<DetailedState, State>(DetailedState.class);
+
+    static {
+        stateMap.put(DetailedState.IDLE, State.DISCONNECTED);
+        stateMap.put(DetailedState.SCANNING, State.DISCONNECTED);
+        stateMap.put(DetailedState.CONNECTING, State.CONNECTING);
+        stateMap.put(DetailedState.AUTHENTICATING, State.CONNECTING);
+        stateMap.put(DetailedState.OBTAINING_IPADDR, State.CONNECTING);
+        stateMap.put(DetailedState.CONNECTED, State.CONNECTED);
+        stateMap.put(DetailedState.SUSPENDED, State.SUSPENDED);
+        stateMap.put(DetailedState.DISCONNECTING, State.DISCONNECTING);
+        stateMap.put(DetailedState.DISCONNECTED, State.DISCONNECTED);
+        stateMap.put(DetailedState.FAILED, State.DISCONNECTED);
+    }
+    
+    private int mNetworkType;
+    private State mState;
+    private DetailedState mDetailedState;
+    private String mReason;
+    private String mExtraInfo;
+    private boolean mIsFailover;
+    /**
+     * Indicates whether network connectivity is possible:
+     */
+    private boolean mIsAvailable;
+
+    public NetworkInfo(int type) {
+        if (!ConnectivityManager.isNetworkTypeValid(type)) {
+            throw new IllegalArgumentException("Invalid network type: " + type);
+        }
+        this.mNetworkType = type;
+        setDetailedState(DetailedState.IDLE, null, null);
+        mState = State.UNKNOWN;
+        mIsAvailable = true;
+    }
+
+    /**
+     * Reports the type of network (currently mobile or Wi-Fi) to which the
+     * info in this object pertains.
+     * @return the network type
+     */
+    public int getType() {
+        return mNetworkType;
+    }
+
+    /**
+     * 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.
+     * For the latter, call {@link #isConnected()} instead, which guarantees
+     * that the network is fully usable.
+     * @return {@code true} if network connectivity exists or is in the process
+     * of being established, {@code false} otherwise.
+     */
+    public boolean isConnectedOrConnecting() {
+        return mState == State.CONNECTED || mState == State.CONNECTING;
+    }
+
+    /**
+     * Indicates whether network connectivity exists and it is possible to establish
+     * connections and pass data.
+     * @return {@code true} if network connectivity exists, {@code false} otherwise.
+     */
+    public boolean isConnected() {
+        return mState == State.CONNECTED;
+    }
+
+    /**
+     * Indicates whether network connectivity is possible. A network is unavailable
+     * when a persistent or semi-persistent condition prevents the possibility
+     * of connecting to that network. Examples include
+     * <ul>
+     * <li>The device is out of the coverage area for any network of this type.</li>
+     * <li>The device is on a network other than the home network (i.e., roaming), and
+     * data roaming has been disabled.</li>
+     * <li>The device's radio is turned off, e.g., because airplane mode is enabled.</li>
+     * </ul>
+     * @return {@code true} if the network is available, {@code false} otherwise
+     */
+    public boolean isAvailable() {
+        return mIsAvailable;
+    }
+
+    /**
+     * Sets if the network is available, ie, if the connectivity is possible.
+     * @param isAvailable the new availability value.
+     *
+     * {@hide}
+     */
+    public void setIsAvailable(boolean isAvailable) {
+        mIsAvailable = isAvailable;
+    }
+
+    /**
+     * Indicates whether the current attempt to connect to the network
+     * resulted from the ConnectivityManager trying to fail over to this
+     * network following a disconnect from another network.
+     * @return {@code true} if this is a failover attempt, {@code false}
+     * otherwise.
+     */
+    public boolean isFailover() {
+        return mIsFailover;
+    }
+
+    /** {@hide} */
+    public void setFailover(boolean isFailover) {
+        mIsFailover = isFailover;
+    }
+
+    /**
+     * Reports the current coarse-grained state of the network.
+     * @return the coarse-grained state
+     */
+    public State getState() {
+        return mState;
+    }
+
+    /**
+     * Reports the current fine-grained state of the network.
+     * @return the fine-grained state
+     */
+    public DetailedState getDetailedState() {
+        return mDetailedState;
+    }
+
+    /**
+     * Sets the fine-grained state of the network.
+     * @param detailedState the {@link DetailedState}.
+     * @param reason a {@code String} indicating the reason for the state change,
+     * if one was supplied. May be {@code null}.
+     * @param extraInfo an optional {@code String} providing addditional network state
+     * information passed up from the lower networking layers.
+     *
+     * {@hide}
+     */
+    void setDetailedState(DetailedState detailedState, String reason, String extraInfo) {
+        this.mDetailedState = detailedState;
+        this.mState = stateMap.get(detailedState);
+        this.mReason = reason;
+        this.mExtraInfo = extraInfo;
+    }
+
+    /**
+     * Report the reason an attempt to establish connectivity failed,
+     * if one is available.
+     * @return the reason for failure, or null if not available
+     */
+    public String getReason() {
+        return mReason;
+    }
+
+    /**
+     * Report the extra information about the network state, if any was
+     * provided by the lower networking layers.,
+     * if one is available.
+     * @return the extra information, or null if not available
+     */
+    public String getExtraInfo() {
+        return mExtraInfo;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("NetworkInfo: ");
+        builder.append("type: ").append(getTypeName()).append(", state: ").append(mState).
+                append("/").append(mDetailedState).
+                append(", reason: ").append(mReason == null ? "(unspecified)" : mReason).
+                append(", extra: ").append(mExtraInfo == null ? "(none)" : mExtraInfo).
+                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} */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mNetworkType);
+        dest.writeString(mState.name());
+        dest.writeString(mDetailedState.name());
+        dest.writeInt(mIsFailover ? 1 : 0);
+        dest.writeInt(mIsAvailable ? 1 : 0);
+        dest.writeString(mReason);
+        dest.writeString(mExtraInfo);
+    }
+
+    /** 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);
+                netInfo.mState = State.valueOf(in.readString());
+                netInfo.mDetailedState = DetailedState.valueOf(in.readString());
+                netInfo.mIsFailover = in.readInt() != 0;
+                netInfo.mIsAvailable = in.readInt() != 0;
+                netInfo.mReason = in.readString();
+                netInfo.mExtraInfo = in.readString();
+                return netInfo;
+            }
+
+            public NetworkInfo[] newArray(int size) {
+                return new NetworkInfo[size];
+            }
+        };
+}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
new file mode 100644
index 0000000..129248a
--- /dev/null
+++ b/core/java/android/net/NetworkUtils.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Native methods for managing network interfaces.
+ *
+ * {@hide}
+ */
+public class NetworkUtils {
+    /** Bring the named network interface down. */
+    public native static int disableInterface(String interfaceName);
+
+    /** Add a route to the specified host via the named interface. */
+    public native static int addHostRoute(String interfaceName, int hostaddr);
+
+    /** Add a default route for the named interface. */
+    public native static int setDefaultRoute(String interfaceName, int gwayAddr);
+
+    /** Return the gateway address for the default route for the named interface. */
+    public native static int getDefaultRoute(String interfaceName);
+
+    /** Remove host routes that uses the named interface. */
+    public native static int removeHostRoutes(String interfaceName);
+
+    /** Remove the default route for the named interface. */
+    public native static int removeDefaultRoute(String interfaceName);
+
+    /** Reset any sockets that are connected via the named interface. */
+    public native static int resetConnections(String interfaceName);
+
+    /**
+     * Start the DHCP client daemon, in order to have it request addresses
+     * for the named interface, and then configure the interface with those
+     * addresses. This call blocks until it obtains a result (either success
+     * or failure) from the daemon.
+     * @param interfaceName the name of the interface to configure
+     * @param ipInfo if the request succeeds, this object is filled in with
+     * the IP address information.
+     * @return {@code true} for success, {@code false} for failure
+     */
+    public native static boolean runDhcp(String interfaceName, DhcpInfo ipInfo);
+
+    /**
+     * Shut down the DHCP client daemon.
+     * @param interfaceName the name of the interface for which the daemon
+     * should be stopped
+     * @return {@code true} for success, {@code false} for failure
+     */
+    public native static boolean stopDhcp(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.
+     * @return the most recent error message, if any
+     */
+    public native static String getDhcpError();
+
+    /**
+     * When static IP configuration has been specified, configure the network
+     * interface according to the values supplied.
+     * @param interfaceName the name of the interface to configure
+     * @param ipInfo the IP address, default gateway, and DNS server addresses
+     * with which to configure the interface.
+     * @return {@code true} for success, {@code false} for failure
+     */
+    public static boolean configureInterface(String interfaceName, DhcpInfo ipInfo) {
+        return configureNative(interfaceName,
+            ipInfo.ipAddress,
+            ipInfo.netmask,
+            ipInfo.gateway,
+            ipInfo.dns1,
+            ipInfo.dns2);
+    }
+
+    private native static boolean configureNative(
+        String interfaceName, int ipAddress, int netmask, int gateway, int dns1, int dns2);
+
+    /**
+     * Look up a host name and return the result as an int. Works if the argument
+     * is an IP address in dot notation. Obviously, this can only be used for IPv4
+     * addresses.
+     * @param hostname the name of the host (or the IP address)
+     * @return the IP address as an {@code int} in network byte order
+     */
+    public static int lookupHost(String hostname) {
+        InetAddress inetAddress;
+        try {
+            inetAddress = InetAddress.getByName(hostname);
+        } catch (UnknownHostException e) {
+            return -1;
+        }
+        byte[] addrBytes;
+        int addr;
+        addrBytes = inetAddress.getAddress();
+        addr = ((addrBytes[3] & 0xff) << 24)
+                | ((addrBytes[2] & 0xff) << 16)
+                | ((addrBytes[1] & 0xff) << 8)
+                |  (addrBytes[0] & 0xff);
+        return addr;
+    }
+}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
new file mode 100644
index 0000000..417ce54
--- /dev/null
+++ b/core/jni/android_net_NetUtils.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+#define LOG_TAG "NetUtils"
+
+#include "jni.h"
+#include <utils/misc.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/Log.h>
+#include <arpa/inet.h>
+
+extern "C" {
+int ifc_disable(const char *ifname);
+int ifc_add_host_route(const char *ifname, uint32_t addr);
+int ifc_remove_host_routes(const char *ifname);
+int ifc_set_default_route(const char *ifname, uint32_t gateway);
+int ifc_get_default_route(const char *ifname);
+int ifc_remove_default_route(const char *ifname);
+int ifc_reset_connections(const char *ifname);
+int ifc_configure(const char *ifname, in_addr_t ipaddr, in_addr_t netmask, in_addr_t gateway, in_addr_t dns1, in_addr_t dns2);
+
+int dhcp_do_request(const char *ifname,
+                    in_addr_t *ipaddr,
+                    in_addr_t *gateway,
+                    in_addr_t *mask,
+                    in_addr_t *dns1,
+                    in_addr_t *dns2,
+                    in_addr_t *server,
+                    uint32_t  *lease);
+int dhcp_stop(const char *ifname);
+char *dhcp_get_errmsg();
+}
+
+#define NETUTILS_PKG_NAME "android/net/NetworkUtils"
+
+namespace android {
+
+/*
+ * The following remembers the jfieldID's of the fields
+ * of the DhcpInfo Java object, so that we don't have
+ * to look them up every time.
+ */
+static struct fieldIds {
+    jclass dhcpInfoClass;
+    jmethodID constructorId;
+    jfieldID ipaddress;
+    jfieldID gateway;
+    jfieldID netmask;
+    jfieldID dns1;
+    jfieldID dns2;
+    jfieldID serverAddress;
+    jfieldID leaseDuration;
+} dhcpInfoFieldIds;
+
+static jint android_net_utils_disableInterface(JNIEnv* env, jobject clazz, jstring ifname)
+{
+    int result;
+
+    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+    result = ::ifc_disable(nameStr);
+    env->ReleaseStringUTFChars(ifname, nameStr);
+    return (jint)result;
+}
+
+static jint android_net_utils_addHostRoute(JNIEnv* env, jobject clazz, jstring ifname, jint addr)
+{
+    int result;
+
+    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+    result = ::ifc_add_host_route(nameStr, addr);
+    env->ReleaseStringUTFChars(ifname, nameStr);
+    return (jint)result;
+}
+
+static jint android_net_utils_removeHostRoutes(JNIEnv* env, jobject clazz, jstring ifname)
+{
+    int result;
+
+    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+    result = ::ifc_remove_host_routes(nameStr);
+    env->ReleaseStringUTFChars(ifname, nameStr);
+    return (jint)result;
+}
+
+static jint android_net_utils_setDefaultRoute(JNIEnv* env, jobject clazz, jstring ifname, jint gateway)
+{
+    int result;
+
+    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+    result = ::ifc_set_default_route(nameStr, gateway);
+    env->ReleaseStringUTFChars(ifname, nameStr);
+    return (jint)result;
+}
+
+static jint android_net_utils_getDefaultRoute(JNIEnv* env, jobject clazz, jstring ifname)
+{
+    int result;
+
+    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+    result = ::ifc_get_default_route(nameStr);
+    env->ReleaseStringUTFChars(ifname, nameStr);
+    return (jint)result;
+}
+
+static jint android_net_utils_removeDefaultRoute(JNIEnv* env, jobject clazz, jstring ifname)
+{
+    int result;
+
+    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+    result = ::ifc_remove_default_route(nameStr);
+    env->ReleaseStringUTFChars(ifname, nameStr);
+    return (jint)result;
+}
+
+static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz, jstring ifname)
+{
+    int result;
+
+    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+    result = ::ifc_reset_connections(nameStr);
+    env->ReleaseStringUTFChars(ifname, nameStr);
+    return (jint)result;
+}
+
+static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
+{
+    int result;
+    in_addr_t ipaddr, gateway, mask, dns1, dns2, server;
+    uint32_t lease;
+
+    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+    result = ::dhcp_do_request(nameStr, &ipaddr, &gateway, &mask,
+                                        &dns1, &dns2, &server, &lease);
+    env->ReleaseStringUTFChars(ifname, nameStr);
+    if (result == 0 && dhcpInfoFieldIds.dhcpInfoClass != NULL) {
+        env->SetIntField(info, dhcpInfoFieldIds.ipaddress, ipaddr);
+        env->SetIntField(info, dhcpInfoFieldIds.gateway, gateway);
+        env->SetIntField(info, dhcpInfoFieldIds.netmask, mask);
+        env->SetIntField(info, dhcpInfoFieldIds.dns1, dns1);
+        env->SetIntField(info, dhcpInfoFieldIds.dns2, dns2);
+        env->SetIntField(info, dhcpInfoFieldIds.serverAddress, server);
+        env->SetIntField(info, dhcpInfoFieldIds.leaseDuration, lease);
+    }
+    return (jboolean)(result == 0);
+}
+
+static jboolean android_net_utils_stopDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
+{
+    int result;
+
+    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+    result = ::dhcp_stop(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());
+}
+
+static jboolean android_net_utils_configureInterface(JNIEnv* env,
+        jobject clazz,
+        jstring ifname,
+        jint ipaddr,
+        jint mask,
+        jint gateway,
+        jint dns1,
+        jint dns2)
+{
+    int result;
+    uint32_t lease;
+
+    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+    result = ::ifc_configure(nameStr, ipaddr, mask, gateway, dns1, dns2);
+    env->ReleaseStringUTFChars(ifname, nameStr);
+    return (jboolean)(result == 0);
+}
+
+// ----------------------------------------------------------------------------
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gNetworkUtilMethods[] = {
+    /* name, signature, funcPtr */
+
+    { "disableInterface", "(Ljava/lang/String;)I",  (void *)android_net_utils_disableInterface },
+    { "addHostRoute", "(Ljava/lang/String;I)I",  (void *)android_net_utils_addHostRoute },
+    { "removeHostRoutes", "(Ljava/lang/String;)I",  (void *)android_net_utils_removeHostRoutes },
+    { "setDefaultRoute", "(Ljava/lang/String;I)I",  (void *)android_net_utils_setDefaultRoute },
+    { "getDefaultRoute", "(Ljava/lang/String;)I",  (void *)android_net_utils_getDefaultRoute },
+    { "removeDefaultRoute", "(Ljava/lang/String;)I",  (void *)android_net_utils_removeDefaultRoute },
+    { "resetConnections", "(Ljava/lang/String;)I",  (void *)android_net_utils_resetConnections },
+    { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z",  (void *)android_net_utils_runDhcp },
+    { "stopDhcp", "(Ljava/lang/String;)Z",  (void *)android_net_utils_stopDhcp },
+    { "configureNative", "(Ljava/lang/String;IIIII)Z",  (void *)android_net_utils_configureInterface },
+    { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
+};
+
+int register_android_net_NetworkUtils(JNIEnv* env)
+{
+    jclass netutils = env->FindClass(NETUTILS_PKG_NAME);
+    LOG_FATAL_IF(netutils == NULL, "Unable to find class " NETUTILS_PKG_NAME);
+
+    dhcpInfoFieldIds.dhcpInfoClass = env->FindClass("android/net/DhcpInfo");
+    if (dhcpInfoFieldIds.dhcpInfoClass != NULL) {
+        dhcpInfoFieldIds.constructorId = env->GetMethodID(dhcpInfoFieldIds.dhcpInfoClass, "<init>", "()V");
+        dhcpInfoFieldIds.ipaddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "ipAddress", "I");
+        dhcpInfoFieldIds.gateway = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "gateway", "I");
+        dhcpInfoFieldIds.netmask = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "netmask", "I");
+        dhcpInfoFieldIds.dns1 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns1", "I");
+        dhcpInfoFieldIds.dns2 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns2", "I");
+        dhcpInfoFieldIds.serverAddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "serverAddress", "I");
+        dhcpInfoFieldIds.leaseDuration = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "leaseDuration", "I");
+    }
+
+    return AndroidRuntime::registerNativeMethods(env,
+            NETUTILS_PKG_NAME, gNetworkUtilMethods, NELEM(gNetworkUtilMethods));
+}
+
+}; // namespace android
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
new file mode 100644
index 0000000..11f34cc
--- /dev/null
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -0,0 +1,693 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
+import android.net.MobileDataStateTracker;
+import android.net.NetworkInfo;
+import android.net.NetworkStateTracker;
+import android.net.wifi.WifiStateTracker;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.util.EventLog;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * {@hide}
+ */
+public class ConnectivityService extends IConnectivityManager.Stub {
+
+    private static final boolean DBG = false;
+    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;
+
+    /**
+     * 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 boolean mTeardownRequested[];
+    private WifiStateTracker mWifiStateTracker;
+    private MobileDataStateTracker mMobileDataStateTracker;
+    private WifiWatchdogService mWifiWatchdogService;
+
+    private Context mContext;
+    private int mNetworkPreference;
+    private NetworkStateTracker mActiveNetwork;
+
+    private int mNumDnsEntries;
+    private static int mDnsChangeCounter;
+
+    private boolean mTestMode;
+    private static ConnectivityService sServiceInstance;
+    
+    private static class ConnectivityThread extends Thread {
+        private Context mContext;
+        
+        private ConnectivityThread(Context context) {
+            super("ConnectivityThread");
+            mContext = context;
+        }
+
+        @Override
+        public void run() {
+            Looper.prepare();
+            synchronized (this) {
+                sServiceInstance = new ConnectivityService(mContext);
+                notifyAll();
+            }
+            Looper.loop();
+        }
+        
+        public static ConnectivityService getServiceInstance(Context context) {
+            ConnectivityThread thread = new ConnectivityThread(context);
+            thread.start();
+            
+            synchronized (thread) {
+                while (sServiceInstance == null) {
+                    try {
+                        // Wait until sServiceInstance has been initialized.
+                        thread.wait();
+                    } catch (InterruptedException e) {
+                    }
+                }
+            }
+            
+            return sServiceInstance;
+        }
+    }
+    
+    public static ConnectivityService getInstance(Context context) {
+        return ConnectivityThread.getServiceInstance(context);
+    }
+    
+    private ConnectivityService(Context context) {
+        if (DBG) Log.v(TAG, "ConnectivityService starting up");
+        mContext = context;
+        mNetTrackers = new NetworkStateTracker[2];
+        mTeardownRequested = new boolean[2];
+        Handler handler = new MyHandler();
+        
+        mNetworkPreference = getPersistedNetworkPreference();
+                
+        /*
+         * Create the network state trackers for Wi-Fi and mobile
+         * data. Maybe this could be done with a factory class,
+         * but it's not clear that it's worth it, given that
+         * the number of different network types is not going
+         * to change very often.
+         */
+        if (DBG) Log.v(TAG, "Starting Wifi Service.");
+        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);
+        mNetTrackers[ConnectivityManager.TYPE_MOBILE] = mMobileDataStateTracker;
+        
+        mActiveNetwork = null;
+        mNumDnsEntries = 0;
+
+        mTestMode = SystemProperties.get("cm.test.mode").equals("true")
+                && SystemProperties.get("ro.build.type").equals("eng");
+
+        for (NetworkStateTracker t : mNetTrackers)
+            t.startMonitoring();
+
+        // Constructing this starts it too
+        mWifiWatchdogService = new WifiWatchdogService(context, mWifiStateTracker);
+    }
+
+    /**
+     * Sets the preferred network. 
+     * @param preference the new preference
+     */
+    public synchronized void setNetworkPreference(int preference) {
+        enforceChangePermission();
+        if (ConnectivityManager.isNetworkTypeValid(preference)) {
+            int oldPreference = mNetworkPreference;
+            persistNetworkPreference(preference);
+            if (mNetworkPreference != oldPreference)
+                enforcePreference();
+        }
+    }
+
+    public int getNetworkPreference() {
+        enforceAccessPermission();
+        return mNetworkPreference;
+    }
+
+    private void persistNetworkPreference(int networkPreference) {
+        final ContentResolver cr = mContext.getContentResolver();
+        Settings.System.putInt(cr, Settings.System.NETWORK_PREFERENCE, networkPreference);
+    }
+    
+    private int getPersistedNetworkPreference() {
+        final ContentResolver cr = mContext.getContentResolver();
+
+        final int networkPrefSetting = Settings.System
+                .getInt(cr, Settings.System.NETWORK_PREFERENCE, -1);
+        if (networkPrefSetting != -1) {
+            return networkPrefSetting;
+        }
+
+        return ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;
+    }
+    
+    /**
+     * Make the state of network connectivity conform to the preference settings.
+     * In this method, we only tear down a non-preferred network. Establishing
+     * a connection to the preferred network is taken care of when we handle
+     * the disconnect event from the non-preferred network
+     * (see {@link #handleDisconnect(NetworkInfo)}).
+     */
+    private void enforcePreference() {
+        if (mActiveNetwork == null)
+            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 (t.getNetworkInfo().getType() != mNetworkPreference) {
+                    NetworkStateTracker otherTracker = mNetTrackers[otherNetType];
+                    if (otherTracker.isAvailable()) {
+                        teardown(t);
+                    }
+                }
+            }
+        }
+    }
+
+    private boolean teardown(NetworkStateTracker netTracker) {
+        if (netTracker.teardown()) {
+            mTeardownRequested[netTracker.getNetworkInfo().getType()] = true;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 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 the info for the active network, or {@code null} if none is active
+     */
+    public NetworkInfo getActiveNetworkInfo() {
+        enforceAccessPermission();
+        for (NetworkStateTracker t : mNetTrackers) {
+            NetworkInfo info = t.getNetworkInfo();
+            if (info.isConnected()) {
+                return info;
+            }
+        }
+        return null;
+    }
+
+    public NetworkInfo getNetworkInfo(int networkType) {
+        enforceAccessPermission();
+        if (ConnectivityManager.isNetworkTypeValid(networkType)) {
+            NetworkStateTracker t = mNetTrackers[networkType];
+            if (t != null)
+                return t.getNetworkInfo();
+        }
+        return null;
+    }
+
+    public NetworkInfo[] getAllNetworkInfo() {
+        enforceAccessPermission();
+        NetworkInfo[] result = new NetworkInfo[mNetTrackers.length];
+        int i = 0;
+        for (NetworkStateTracker t : mNetTrackers) {
+            result[i++] = t.getNetworkInfo();
+        }
+        return result;
+    }
+
+    public boolean setRadios(boolean turnOn) {
+        boolean result = true;
+        enforceChangePermission();
+        for (NetworkStateTracker t : mNetTrackers) {
+            result = t.setRadio(turnOn) && result;
+        }
+        return result;
+    }
+
+    public boolean setRadio(int netType, boolean turnOn) {
+        enforceChangePermission();
+        if (!ConnectivityManager.isNetworkTypeValid(netType)) {
+            return false;
+        }
+        NetworkStateTracker tracker = mNetTrackers[netType];
+        return tracker != null && tracker.setRadio(turnOn);
+    }
+
+    public int startUsingNetworkFeature(int networkType, String feature) {
+        enforceChangePermission();
+        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
+            return -1;
+        }
+        NetworkStateTracker tracker = mNetTrackers[networkType];
+        if (tracker != null) {
+            return tracker.startUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
+        }
+        return -1;
+
+    }
+
+    public int stopUsingNetworkFeature(int networkType, String feature) {
+        enforceChangePermission();
+        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
+            return -1;
+        }
+        NetworkStateTracker tracker = mNetTrackers[networkType];
+        if (tracker != null) {
+            return tracker.stopUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
+        }
+        return -1;
+    }
+
+    /**
+     * Ensure that a network route exists to deliver traffic to the specified
+     * host via the specified network interface.
+     * @param networkType the type of the network over which traffic to the specified
+     * host is to be routed
+     * @param hostAddress the IP address of the host to which the route is desired
+     * @return {@code true} on success, {@code false} on failure
+     */
+    public boolean requestRouteToHost(int networkType, int hostAddress) {
+        enforceChangePermission();
+        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
+            return false;
+        }
+        NetworkStateTracker tracker = mNetTrackers[networkType];
+        /*
+         * If there's only one connected network, and it's the one requested,
+         * then we don't have to do anything - the requested route already
+         * exists. If it's not the requested network, then it's not possible
+         * to establish the requested route. Finally, if there is more than
+         * one connected network, then we must insert an entry in the routing
+         * table.
+         */
+        if (getNumConnectedNetworks() > 1) {
+            return tracker.requestRouteToHost(hostAddress);
+        } else {
+            return tracker.getNetworkInfo().getType() == networkType;
+        }
+    }
+
+    private int getNumConnectedNetworks() {
+        int numConnectedNets = 0;
+
+        for (NetworkStateTracker nt : mNetTrackers) {
+            if (nt.getNetworkInfo().isConnected()
+                    && !mTeardownRequested[nt.getNetworkInfo().getType()]) {
+                ++numConnectedNets;
+            }
+        }
+        return numConnectedNets;
+    }
+
+    private void enforceAccessPermission() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
+                                          "ConnectivityService");
+    }
+
+    private void enforceChangePermission() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE,
+                                          "ConnectivityService");
+
+    }
+
+    /**
+     * Handle a {@code DISCONNECTED} event. If this pertains to the non-active network,
+     * we ignore it. If it is for the active network, we send out a broadcast.
+     * But first, we check whether it might be possible to connect to a different
+     * network.
+     * @param info the {@code NetworkInfo} for the network
+     */
+    private void handleDisconnect(NetworkInfo info) {
+
+        if (DBG) Log.v(TAG, "Handle DISCONNECT for " + info.getTypeName());
+
+        /*
+         * 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;
+
+        NetworkStateTracker newNet;
+        if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
+            newNet = mWifiStateTracker;
+        } else /* info().getType() == TYPE_WIFI */ {
+            newNet = mMobileDataStateTracker;
+        }
+
+        NetworkInfo switchTo = null;
+        if (newNet.isAvailable()) {
+            mActiveNetwork = newNet;
+            switchTo = newNet.getNetworkInfo();
+            switchTo.setFailover(true);
+            if (!switchTo.isConnectedOrConnecting())
+                newNet.reconnect();
+        }
+
+        boolean otherNetworkConnected = false;
+        Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+        if (info.isFailover()) {
+            intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
+            info.setFailover(false);
+        }
+        if (info.getReason() != null) {
+            intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
+        }
+        if (info.getExtraInfo() != null) {
+            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());
+                }
+            }
+            intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
+        } else {
+            intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
+        }
+        if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " + info.getTypeName() +
+                (switchTo == null ? "" : " other=" + switchTo.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);
+    }
+
+    private void sendConnectedBroadcast(NetworkInfo info) {
+        Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+        if (info.isFailover()) {
+            intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
+            info.setFailover(false);
+        }
+        if (info.getReason() != null) {
+            intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
+        }
+        if (info.getExtraInfo() != null) {
+            intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
+        }
+        mContext.sendStickyBroadcast(intent);
+    }
+
+    private void handleConnectionFailure(NetworkInfo info) {
+        mTeardownRequested[info.getType()] = 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);
+            }
+            
+            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 (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());
+
+        // 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 now connected to a network that
+        * takes precedence over the other one. If so, we need to tear down
+        * the other one.
+        */
+        NetworkInfo wifiInfo = mWifiStateTracker.getNetworkInfo();
+        NetworkInfo mobileInfo = mMobileDataStateTracker.getNetworkInfo();
+        if (wifiInfo.isConnected() && mobileInfo.isConnected()) {
+            if (mNetworkPreference == ConnectivityManager.TYPE_WIFI)
+                deadnet = mMobileDataStateTracker;
+            else
+                deadnet = mWifiStateTracker;
+        }
+
+        boolean toredown = false;
+        mTeardownRequested[info.getType()] = 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");
+            }
+        }
+
+        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());
+        }
+    }
+
+    private void handleScanResultsAvailable(NetworkInfo info) {
+        int networkType = info.getType();
+        if (networkType != ConnectivityManager.TYPE_WIFI) {
+            if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " + info.getTypeName() + " network."
+                + " Don't know how to handle.");
+        }
+        
+        mNetTrackers[networkType].interpretScanResultsAvailable();
+    }
+
+    private void handleNotificationChange(boolean visible, int id, Notification notification) {
+        NotificationManager notificationManager = (NotificationManager) mContext
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+        
+        if (visible) {
+            notificationManager.notify(id, notification);
+        } else {
+            notificationManager.cancel(id);
+        }
+    }
+
+    /**
+     * After any kind of change in the connectivity state of any network,
+     * make sure that anything that depends on the connectivity state of
+     * more than one network is set up correctly. We're mainly concerned
+     * with making sure that the list of DNS servers is set up  according
+     * to which networks are connected, and ensuring that the right routing
+     * table entries exist.
+     */
+    private void handleConnectivityChange() {
+        /*
+         * 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();
+        }
+    }
+
+    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 net = ConnectivityManager.TYPE_WIFI; net != stopValue; net += incrValue) {            
+            NetworkStateTracker nt = mNetTrackers[net];
+            if (nt.getNetworkInfo().isConnected()
+                    && !mTeardownRequested[nt.getNetworkInfo().getType()]) {
+                ++numConnectedNets;
+                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];
+                    }
+                }
+            }
+        }
+        // Null out any DNS properties that are no longer used
+        for (int i = index; i <= mNumDnsEntries; i++) {
+            SystemProperties.set("net.dns" + i, "");
+        }
+        mNumDnsEntries = index - 1;
+        // Notify the name resolver library of the change
+        SystemProperties.set("net.dnschange", String.valueOf(mDnsChangeCounter++));
+        return numConnectedNets;
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump ConnectivityService from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + 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) {
+            pw.println(nst.getNetworkInfo());
+            pw.println(nst);
+            pw.println();
+        }
+    }
+
+    private class MyHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            NetworkInfo info;
+            switch (msg.what) {
+                case NetworkStateTracker.EVENT_STATE_CHANGED:
+                    info = (NetworkInfo) msg.obj;
+                    if (DBG) Log.v(TAG, "ConnectivityChange for " + info.getTypeName() + ": " +
+                            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)   
+                    // [2-0] Network type (as defined by ConnectivityManager)
+                    int eventLogParam = (info.getType() & 0x7) |
+                         ((info.getDetailedState().ordinal() & 0x3f) << 3) |
+                         (TelephonyManager.getDefault().getNetworkType() << 9);
+                    EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED, eventLogParam);
+                    
+                    if (info.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
+                        handleConnectionFailure(info);
+                    } else if (info.getState() == NetworkInfo.State.DISCONNECTED) {
+                        handleDisconnect(info);
+                    } else if (info.getState() == NetworkInfo.State.SUSPENDED) {
+                        // TODO: need to think this over.
+                        // the logic here is, handle SUSPENDED the same as DISCONNECTED. The
+                        // only difference being we are broadcasting an intent with NetworkInfo
+                        // that's suspended. This allows the applications an opportunity to
+                        // handle DISCONNECTED and SUSPENDED differently, or not.
+                        handleDisconnect(info);
+                    } else if (info.getState() == NetworkInfo.State.CONNECTED) {
+                        handleConnect(info);
+                    }
+                    handleConnectivityChange();
+                    break;
+
+                case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
+                    info = (NetworkInfo) msg.obj;
+                    handleScanResultsAvailable(info);
+                    break;
+                    
+                case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
+                    handleNotificationChange(msg.arg1 == 1, msg.arg2, (Notification) msg.obj);
+
+                case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
+                    handleConfigurationChange();
+                    break;
+            }
+        }
+    }
+}