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;
+ }
+ }
+ }
+}