resolved conflicts for merge of 6ea0082b to master
Change-Id: I8ec28728c12d7cc3ce2c4f3d09d9ce6162504618
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 4eecfa9..8de545e 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -13,22 +13,40 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package android.net;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkUtils;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.INetworkActivityListener;
+import android.os.INetworkManagementService;
+import android.os.Looper;
+import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import android.os.ResultReceiver;
+import android.os.ServiceManager;
import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.util.Protocol;
import java.net.InetAddress;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.HashMap;
/**
* Class that answers queries about the state of network connectivity. It also
@@ -44,13 +62,16 @@
* is lost</li>
* <li>Provide an API that allows applications to query the coarse-grained or fine-grained
* state of the available networks</li>
+ * <li>Provide an API that allows applications to request and select networks for their data
+ * traffic</li>
* </ol>
*/
public class ConnectivityManager {
private static final String TAG = "ConnectivityManager";
+ private static final boolean LEGACY_DBG = true; // STOPSHIP
/**
- * A change in network connectivity has occurred. A connection has either
+ * A change in network connectivity has occurred. A default 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.
@@ -77,7 +98,7 @@
/**
* Identical to {@link #CONNECTIVITY_ACTION} broadcast, but sent without any
- * applicable {@link Settings.Secure#CONNECTIVITY_CHANGE_DELAY}.
+ * applicable {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY}.
*
* @hide
*/
@@ -171,6 +192,11 @@
* {@hide}
*/
public static final String EXTRA_IS_ACTIVE = "isActive";
+ /**
+ * The lookup key for a long that contains the timestamp (nanos) of the radio state change.
+ * {@hide}
+ */
+ public static final String EXTRA_REALTIME_NS = "tsNanos";
/**
* Broadcast Action: The setting for background data usage has changed
@@ -258,6 +284,98 @@
public static final String EXTRA_IS_CAPTIVE_PORTAL = "captivePortal";
/**
+ * Broadcast Action: A connection has been established to a new network
+ * but a captive portal has been detected preventing internet connectivity.
+ * This broadcast is sent out prior to providing the user with a
+ * notification allowing them to sign into the network, as such it should
+ * only be used by apps that can automatically and silently (without user
+ * interaction) log into specific captive portals. It should not be used
+ * by apps that prompt the user to sign in, as the user has not yet
+ * indicated they want to proceed with signing in.
+ * The new network is not the default network so it can only be accessed via
+ * the {@link Network} extra {@link #EXTRA_NETWORK}.
+ * This is an ordered broadcast and so it is perfectly acceptable for
+ * multiple receivers to in turn consider whether they are best suited to
+ * address the captive portal.
+ * A receiver should abort the broadcast if they are sure they are the
+ * appropriate handler of the captive portal. If the broadcast is aborted,
+ * the result code must be set to one of the following:
+ * <ul>
+ * <li>{@link #CAPTIVE_PORTAL_SIGNED_IN} The receiver has signed into the
+ * captive portal. After being verified to provide internet
+ * connectivity, this network will be made the default (assuming
+ * it is preferred over all other active networks).
+ * </li>
+ * <li>{@link #CAPTIVE_PORTAL_DISCONNECT} The receiver is familiar with
+ * this captive portal and knows sign-in is impossible or the user
+ * has indicated they do not want to pursue sign-in. No other apps
+ * will be given the option of signing in to the network.
+ * </li>
+ * </ul>
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CAPTIVE_PORTAL_DETECTED =
+ "android.net.conn.CAPTIVE_PORTAL_DETECTED";
+
+ /**
+ * Broadcast Action: A connection has been established to a new network,
+ * a captive portal has been detected preventing internet connectivity,
+ * the user was notified, and elected to sign into the captive portal.
+ * It may be used by apps that prompt the user to sign in.
+ * The new network is not the default network so it can only be accessed via
+ * the {@link Network} extra {@link #EXTRA_NETWORK}.
+ * This is an ordered broadcast and so it is perfectly acceptable for
+ * multiple receivers to in turn consider whether they are best suited to
+ * address the captive portal.
+ * A receiver should abort the broadcast if they are sure they are the
+ * appropriate handler of the captive portal. If the broadcast is aborted,
+ * the result code must be set to one of the following:
+ * <ul>
+ * <li>{@link #CAPTIVE_PORTAL_SIGNED_IN} The receiver has signed into the
+ * captive portal. After being verified to provide internet
+ * connectivity, this network will be made the default (assuming
+ * it is preferred over all other active networks).
+ * </li>
+ * <li>{@link #CAPTIVE_PORTAL_DISCONNECT} The user has indicated they do
+ * not want to pursue sign-in. No other apps will be given the
+ * option of signing in to the network.
+ * </li>
+ * </ul>
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN =
+ "android.net.conn.CAPTIVE_PORTAL_SIGN_IN";
+
+ /**
+ * The lookup key for a {@link Network} object passed along with a
+ * {@link #ACTION_CAPTIVE_PORTAL_DETECTED} or
+ * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} intent. This network is not the
+ * default network and must be accessed using this {@link Network} object.
+ * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
+ */
+ public static final String EXTRA_NETWORK = "network";
+
+ /**
+ * Specified as a result code of a {@link #ACTION_CAPTIVE_PORTAL_DETECTED} or
+ * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} receiver to indicate
+ * the receiver has signed into the
+ * captive portal. After being verified to provide internet
+ * connectivity, this network will be made the default (assuming
+ * it is preferred over all other active networks).
+ */
+ public static final int CAPTIVE_PORTAL_SIGNED_IN = 1;
+
+ /**
+ * Specified as a result code of a {@link #ACTION_CAPTIVE_PORTAL_DETECTED} or
+ * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} receiver to indicate
+ * the receiver is familiar with
+ * this captive portal and knows sign-in is impossible or the user
+ * has indicated they do not want to pursue sign-in. No other apps will
+ * be given the option of signing in to the network.
+ */
+ public static final int CAPTIVE_PORTAL_DISCONNECT = 2;
+
+ /**
* The absence of a connection type.
* @hide
*/
@@ -361,17 +479,29 @@
*/
public static final int TYPE_MOBILE_IA = 14;
+/**
+ * Emergency PDN connection for emergency calls
+ * {@hide}
+ */
+ public static final int TYPE_MOBILE_EMERGENCY = 15;
+
/**
* The network that uses proxy to achieve connectivity.
* {@hide}
*/
public static final int TYPE_PROXY = 16;
- /** {@hide} */
- public static final int MAX_RADIO_TYPE = TYPE_PROXY;
+ /**
+ * A virtual network using one or more native bearers.
+ * It may or may not be providing security services.
+ */
+ public static final int TYPE_VPN = 17;
/** {@hide} */
- public static final int MAX_NETWORK_TYPE = TYPE_PROXY;
+ public static final int MAX_RADIO_TYPE = TYPE_VPN;
+
+ /** {@hide} */
+ public static final int MAX_NETWORK_TYPE = TYPE_VPN;
/**
* If you want to set the default network preference,you can directly
@@ -399,10 +529,24 @@
*/
public static final int CONNECTIVITY_CHANGE_DELAY_DEFAULT = 3000;
+ /**
+ * @hide
+ */
+ public final static int REQUEST_ID_UNSET = 0;
+
+ /**
+ * A NetID indicating no Network is selected.
+ * Keep in sync with bionic/libc/dns/include/resolv_netid.h
+ * @hide
+ */
+ public static final int NETID_UNSET = 0;
+
private final IConnectivityManager mService;
private final String mPackageName;
+ private INetworkManagementService mNMService;
+
/**
* Tests if a given integer represents a valid network type.
* @param networkType the type to be tested
@@ -452,6 +596,8 @@
return "WIFI_P2P";
case TYPE_MOBILE_IA:
return "MOBILE_IA";
+ case TYPE_MOBILE_EMERGENCY:
+ return "MOBILE_EMERGENCY";
case TYPE_PROXY:
return "PROXY";
default:
@@ -477,6 +623,7 @@
case TYPE_MOBILE_IMS:
case TYPE_MOBILE_CBS:
case TYPE_MOBILE_IA:
+ case TYPE_MOBILE_EMERGENCY:
return true;
default:
return false;
@@ -518,38 +665,32 @@
/**
* Specifies the preferred network type. When the device has more
* than one type available the preferred network type will be used.
- * Note that this made sense when we only had 2 network types,
- * but with more and more default networks we need an array to list
- * their ordering. This will be deprecated soon.
*
* @param preference the network type to prefer over all others. It is
* unspecified what happens to the old preferred network in the
* overall ordering.
+ * @deprecated Functionality has been removed as it no longer makes sense,
+ * with many more than two networks - we'd need an array to express
+ * preference. Instead we use dynamic network properties of
+ * the networks to describe their precedence.
*/
public void setNetworkPreference(int preference) {
- try {
- mService.setNetworkPreference(preference);
- } catch (RemoteException e) {
- }
}
/**
* Retrieves the current preferred network type.
- * Note that this made sense when we only had 2 network types,
- * but with more and more default networks we need an array to list
- * their ordering. This will be deprecated soon.
*
* @return an integer representing the preferred network type
*
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+ * @deprecated Functionality has been removed as it no longer makes sense,
+ * with many more than two networks - we'd need an array to express
+ * preference. Instead we use dynamic network properties of
+ * the networks to describe their precedence.
*/
public int getNetworkPreference() {
- try {
- return mService.getNetworkPreference();
- } catch (RemoteException e) {
- return -1;
- }
+ return TYPE_NONE;
}
/**
@@ -604,7 +745,7 @@
* network type or {@code null} if the type is not
* supported by the device.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
*/
public NetworkInfo getNetworkInfo(int networkType) {
@@ -616,13 +757,34 @@
}
/**
+ * Returns connection status information about a particular
+ * Network.
+ *
+ * @param network {@link Network} specifying which network
+ * in which you're interested.
+ * @return a {@link NetworkInfo} object for the requested
+ * network or {@code null} if the {@code Network}
+ * is not valid.
+ *
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+ */
+ public NetworkInfo getNetworkInfo(Network network) {
+ try {
+ return mService.getNetworkInfoForNetwork(network);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
* Returns connection status information about all network
* types supported by the device.
*
* @return an array of {@link NetworkInfo} objects. Check each
* {@link NetworkInfo#getType} for which type each applies.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
*/
public NetworkInfo[] getAllNetworkInfo() {
@@ -634,6 +796,23 @@
}
/**
+ * Returns an array of all {@link Network} currently tracked by the
+ * framework.
+ *
+ * @return an array of {@link Network} objects.
+ *
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+ */
+ public Network[] getAllNetworks() {
+ try {
+ return mService.getAllNetworks();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
* Returns details about the Provisioning or currently active default data network. When
* connected, this network is the default route for outgoing connections.
* You should always check {@link NetworkInfo#isConnected()} before initiating
@@ -689,7 +868,37 @@
*/
public LinkProperties getLinkProperties(int networkType) {
try {
- return mService.getLinkProperties(networkType);
+ return mService.getLinkPropertiesForType(networkType);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Get the {@link LinkProperties} for the given {@link Network}. This
+ * will return {@code null} if the network is unknown.
+ *
+ * @param network The {@link Network} object identifying the network in question.
+ * @return The {@link LinkProperties} for the network, or {@code null}.
+ **/
+ public LinkProperties getLinkProperties(Network network) {
+ try {
+ return mService.getLinkProperties(network);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Get the {@link NetworkCapabilities} for the given {@link Network}. This
+ * will return {@code null} if the network is unknown.
+ *
+ * @param network The {@link Network} object identifying the network in question.
+ * @return The {@link NetworkCapabilities} for the network, or {@code null}.
+ */
+ public NetworkCapabilities getNetworkCapabilities(Network network) {
+ try {
+ return mService.getNetworkCapabilities(network);
} catch (RemoteException e) {
return null;
}
@@ -707,13 +916,14 @@
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
* {@hide}
*/
- public boolean setRadios(boolean turnOn) {
- try {
- return mService.setRadios(turnOn);
- } catch (RemoteException e) {
- return false;
- }
- }
+// TODO - check for any callers and remove
+// public boolean setRadios(boolean turnOn) {
+// try {
+// return mService.setRadios(turnOn);
+// } catch (RemoteException e) {
+// return false;
+// }
+// }
/**
* Tells a given networkType to set its radio power state as directed.
@@ -727,13 +937,14 @@
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
* {@hide}
*/
- public boolean setRadio(int networkType, boolean turnOn) {
- try {
- return mService.setRadio(networkType, turnOn);
- } catch (RemoteException e) {
- return false;
- }
- }
+// TODO - check for any callers and remove
+// 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
@@ -747,13 +958,46 @@
* The interpretation of this value is specific to each networking
* implementation+feature combination, except that the value {@code -1}
* always indicates failure.
+ *
+ * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
*/
public int startUsingNetworkFeature(int networkType, String feature) {
- try {
- return mService.startUsingNetworkFeature(networkType, feature,
- new Binder());
- } catch (RemoteException e) {
- return -1;
+ NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
+ if (netCap == null) {
+ Log.d(TAG, "Can't satisfy startUsingNetworkFeature for " + networkType + ", " +
+ feature);
+ return PhoneConstants.APN_REQUEST_FAILED;
+ }
+
+ NetworkRequest request = null;
+ synchronized (sLegacyRequests) {
+ if (LEGACY_DBG) {
+ Log.d(TAG, "Looking for legacyRequest for netCap with hash: " + netCap + " (" +
+ netCap.hashCode() + ")");
+ Log.d(TAG, "sLegacyRequests has:");
+ for (NetworkCapabilities nc : sLegacyRequests.keySet()) {
+ Log.d(TAG, " " + nc + " (" + nc.hashCode() + ")");
+ }
+ }
+ LegacyRequest l = sLegacyRequests.get(netCap);
+ if (l != null) {
+ Log.d(TAG, "renewing startUsingNetworkFeature request " + l.networkRequest);
+ renewRequestLocked(l);
+ if (l.currentNetwork != null) {
+ return PhoneConstants.APN_ALREADY_ACTIVE;
+ } else {
+ return PhoneConstants.APN_REQUEST_STARTED;
+ }
+ }
+
+ request = requestNetworkForFeatureLocked(netCap);
+ }
+ if (request != null) {
+ Log.d(TAG, "starting startUsingNetworkFeature for request " + request);
+ return PhoneConstants.APN_REQUEST_STARTED;
+ } else {
+ Log.d(TAG, " request Failed");
+ return PhoneConstants.APN_REQUEST_FAILED;
}
}
@@ -769,13 +1013,212 @@
* The interpretation of this value is specific to each networking
* implementation+feature combination, except that the value {@code -1}
* always indicates failure.
+ *
+ * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
*/
public int stopUsingNetworkFeature(int networkType, String feature) {
- try {
- return mService.stopUsingNetworkFeature(networkType, feature);
- } catch (RemoteException e) {
+ NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
+ if (netCap == null) {
+ Log.d(TAG, "Can't satisfy stopUsingNetworkFeature for " + networkType + ", " +
+ feature);
return -1;
}
+
+ NetworkCallback networkCallback = removeRequestForFeature(netCap);
+ if (networkCallback != null) {
+ Log.d(TAG, "stopUsingNetworkFeature for " + networkType + ", " + feature);
+ unregisterNetworkCallback(networkCallback);
+ }
+ return 1;
+ }
+
+ /**
+ * Removes the NET_CAPABILITY_NOT_RESTRICTED capability from the given
+ * NetworkCapabilities object if all the capabilities it provides are
+ * typically provided by restricted networks.
+ *
+ * TODO: consider:
+ * - Moving to NetworkCapabilities
+ * - Renaming it to guessRestrictedCapability and make it set the
+ * restricted capability bit in addition to clearing it.
+ * @hide
+ */
+ public static void maybeMarkCapabilitiesRestricted(NetworkCapabilities nc) {
+ for (int capability : nc.getCapabilities()) {
+ switch (capability) {
+ case NetworkCapabilities.NET_CAPABILITY_CBS:
+ case NetworkCapabilities.NET_CAPABILITY_DUN:
+ case NetworkCapabilities.NET_CAPABILITY_EIMS:
+ case NetworkCapabilities.NET_CAPABILITY_FOTA:
+ case NetworkCapabilities.NET_CAPABILITY_IA:
+ case NetworkCapabilities.NET_CAPABILITY_IMS:
+ case NetworkCapabilities.NET_CAPABILITY_RCS:
+ case NetworkCapabilities.NET_CAPABILITY_XCAP:
+ case NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED: //there by default
+ continue;
+ default:
+ // At least one capability usually provided by unrestricted
+ // networks. Conclude that this network is unrestricted.
+ return;
+ }
+ }
+ // All the capabilities are typically provided by restricted networks.
+ // Conclude that this network is restricted.
+ nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ }
+
+ private NetworkCapabilities networkCapabilitiesForFeature(int networkType, String feature) {
+ if (networkType == TYPE_MOBILE) {
+ int cap = -1;
+ if ("enableMMS".equals(feature)) {
+ cap = NetworkCapabilities.NET_CAPABILITY_MMS;
+ } else if ("enableSUPL".equals(feature)) {
+ cap = NetworkCapabilities.NET_CAPABILITY_SUPL;
+ } else if ("enableDUN".equals(feature) || "enableDUNAlways".equals(feature)) {
+ cap = NetworkCapabilities.NET_CAPABILITY_DUN;
+ } else if ("enableHIPRI".equals(feature)) {
+ cap = NetworkCapabilities.NET_CAPABILITY_INTERNET;
+ } else if ("enableFOTA".equals(feature)) {
+ cap = NetworkCapabilities.NET_CAPABILITY_FOTA;
+ } else if ("enableIMS".equals(feature)) {
+ cap = NetworkCapabilities.NET_CAPABILITY_IMS;
+ } else if ("enableCBS".equals(feature)) {
+ cap = NetworkCapabilities.NET_CAPABILITY_CBS;
+ } else {
+ return null;
+ }
+ NetworkCapabilities netCap = new NetworkCapabilities();
+ netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).addCapability(cap);
+ maybeMarkCapabilitiesRestricted(netCap);
+ return netCap;
+ } else if (networkType == TYPE_WIFI) {
+ if ("p2p".equals(feature)) {
+ NetworkCapabilities netCap = new NetworkCapabilities();
+ netCap.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P);
+ maybeMarkCapabilitiesRestricted(netCap);
+ return netCap;
+ }
+ }
+ return null;
+ }
+
+ private int legacyTypeForNetworkCapabilities(NetworkCapabilities netCap) {
+ if (netCap == null) return TYPE_NONE;
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
+ return TYPE_MOBILE_CBS;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
+ return TYPE_MOBILE_IMS;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
+ return TYPE_MOBILE_FOTA;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
+ return TYPE_MOBILE_DUN;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
+ return TYPE_MOBILE_SUPL;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
+ return TYPE_MOBILE_MMS;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ return TYPE_MOBILE_HIPRI;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P)) {
+ return TYPE_WIFI_P2P;
+ }
+ return TYPE_NONE;
+ }
+
+ private static class LegacyRequest {
+ NetworkCapabilities networkCapabilities;
+ NetworkRequest networkRequest;
+ int expireSequenceNumber;
+ Network currentNetwork;
+ int delay = -1;
+ NetworkCallback networkCallback = new NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ currentNetwork = network;
+ Log.d(TAG, "startUsingNetworkFeature got Network:" + network);
+ setProcessDefaultNetworkForHostResolution(network);
+ }
+ @Override
+ public void onLost(Network network) {
+ if (network.equals(currentNetwork)) {
+ currentNetwork = null;
+ setProcessDefaultNetworkForHostResolution(null);
+ }
+ Log.d(TAG, "startUsingNetworkFeature lost Network:" + network);
+ }
+ };
+ }
+
+ private HashMap<NetworkCapabilities, LegacyRequest> sLegacyRequests =
+ new HashMap<NetworkCapabilities, LegacyRequest>();
+
+ private NetworkRequest findRequestForFeature(NetworkCapabilities netCap) {
+ synchronized (sLegacyRequests) {
+ LegacyRequest l = sLegacyRequests.get(netCap);
+ if (l != null) return l.networkRequest;
+ }
+ return null;
+ }
+
+ private void renewRequestLocked(LegacyRequest l) {
+ l.expireSequenceNumber++;
+ Log.d(TAG, "renewing request to seqNum " + l.expireSequenceNumber);
+ sendExpireMsgForFeature(l.networkCapabilities, l.expireSequenceNumber, l.delay);
+ }
+
+ private void expireRequest(NetworkCapabilities netCap, int sequenceNum) {
+ int ourSeqNum = -1;
+ synchronized (sLegacyRequests) {
+ LegacyRequest l = sLegacyRequests.get(netCap);
+ if (l == null) return;
+ ourSeqNum = l.expireSequenceNumber;
+ if (l.expireSequenceNumber == sequenceNum) {
+ unregisterNetworkCallback(l.networkCallback);
+ sLegacyRequests.remove(netCap);
+ }
+ }
+ Log.d(TAG, "expireRequest with " + ourSeqNum + ", " + sequenceNum);
+ }
+
+ private NetworkRequest requestNetworkForFeatureLocked(NetworkCapabilities netCap) {
+ int delay = -1;
+ int type = legacyTypeForNetworkCapabilities(netCap);
+ try {
+ delay = mService.getRestoreDefaultNetworkDelay(type);
+ } catch (RemoteException e) {}
+ LegacyRequest l = new LegacyRequest();
+ l.networkCapabilities = netCap;
+ l.delay = delay;
+ l.expireSequenceNumber = 0;
+ l.networkRequest = sendRequestForNetwork(netCap, l.networkCallback, 0,
+ REQUEST, type);
+ if (l.networkRequest == null) return null;
+ sLegacyRequests.put(netCap, l);
+ sendExpireMsgForFeature(netCap, l.expireSequenceNumber, delay);
+ return l.networkRequest;
+ }
+
+ private void sendExpireMsgForFeature(NetworkCapabilities netCap, int seqNum, int delay) {
+ if (delay >= 0) {
+ Log.d(TAG, "sending expire msg with seqNum " + seqNum + " and delay " + delay);
+ Message msg = sCallbackHandler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap);
+ sCallbackHandler.sendMessageDelayed(msg, delay);
+ }
+ }
+
+ private NetworkCallback removeRequestForFeature(NetworkCapabilities netCap) {
+ synchronized (sLegacyRequests) {
+ LegacyRequest l = sLegacyRequests.remove(netCap);
+ if (l == null) return null;
+ return l.networkCallback;
+ }
}
/**
@@ -788,6 +1231,9 @@
* 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
+ *
+ * @deprecated Deprecated in favor of the {@link #requestNetwork},
+ * {@link #setProcessDefaultNetwork} and {@link Network#getSocketFactory} api.
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
InetAddress inetAddress = NetworkUtils.intToInetAddress(hostAddress);
@@ -803,11 +1249,15 @@
* 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.
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
* @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
* @hide
+ * @deprecated Deprecated in favor of the {@link #requestNetwork} and
+ * {@link #setProcessDefaultNetwork} api.
*/
public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
byte[] address = hostAddress.getAddress();
@@ -875,37 +1325,112 @@
}
/**
- * Gets the value of the setting for enabling Mobile data.
- *
- * @return Whether mobile data is enabled.
- *
- * <p>This method requires the call to hold the permission
- * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* @hide
+ * @deprecated Talk to TelephonyManager directly
*/
public boolean getMobileDataEnabled() {
+ IBinder b = ServiceManager.getService(Context.TELEPHONY_SERVICE);
+ if (b != null) {
+ try {
+ ITelephony it = ITelephony.Stub.asInterface(b);
+ return it.getDataEnabled();
+ } catch (RemoteException e) { }
+ }
+ return false;
+ }
+
+ /**
+ * Callback for use with {@link ConnectivityManager#registerDefaultNetworkActiveListener}
+ * to find out when the system default network has gone in to a high power state.
+ */
+ public interface OnNetworkActiveListener {
+ /**
+ * Called on the main thread of the process to report that the current data network
+ * has become active, and it is now a good time to perform any pending network
+ * operations. Note that this listener only tells you when the network becomes
+ * active; if at any other time you want to know whether it is active (and thus okay
+ * to initiate network traffic), you can retrieve its instantaneous state with
+ * {@link ConnectivityManager#isDefaultNetworkActive}.
+ */
+ public void onNetworkActive();
+ }
+
+ private INetworkManagementService getNetworkManagementService() {
+ synchronized (this) {
+ if (mNMService != null) {
+ return mNMService;
+ }
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ mNMService = INetworkManagementService.Stub.asInterface(b);
+ return mNMService;
+ }
+ }
+
+ private final ArrayMap<OnNetworkActiveListener, INetworkActivityListener>
+ mNetworkActivityListeners
+ = new ArrayMap<OnNetworkActiveListener, INetworkActivityListener>();
+
+ /**
+ * Start listening to reports when the system's default data network is active, meaning it is
+ * a good time to perform network traffic. Use {@link #isDefaultNetworkActive()}
+ * to determine the current state of the system's default network after registering the
+ * listener.
+ * <p>
+ * If the process default network has been set with
+ * {@link ConnectivityManager#setProcessDefaultNetwork} this function will not
+ * reflect the process's default, but the system default.
+ *
+ * @param l The listener to be told when the network is active.
+ */
+ public void registerDefaultNetworkActiveListener(final OnNetworkActiveListener l) {
+ INetworkActivityListener rl = new INetworkActivityListener.Stub() {
+ @Override
+ public void onNetworkActive() throws RemoteException {
+ l.onNetworkActive();
+ }
+ };
+
try {
- return mService.getMobileDataEnabled();
+ getNetworkManagementService().registerNetworkActivityListener(rl);
+ mNetworkActivityListeners.put(l, rl);
} catch (RemoteException e) {
- return true;
}
}
/**
- * Sets the persisted value for enabling/disabling Mobile data.
+ * Remove network active listener previously registered with
+ * {@link #registerDefaultNetworkActiveListener}.
*
- * @param enabled Whether the user wants the mobile data connection used
- * or not.
- * @hide
+ * @param l Previously registered listener.
*/
- public void setMobileDataEnabled(boolean enabled) {
+ public void unregisterDefaultNetworkActiveListener(OnNetworkActiveListener l) {
+ INetworkActivityListener rl = mNetworkActivityListeners.get(l);
+ if (rl == null) {
+ throw new IllegalArgumentException("Listener not registered: " + l);
+ }
try {
- mService.setMobileDataEnabled(enabled);
+ getNetworkManagementService().unregisterNetworkActivityListener(rl);
} catch (RemoteException e) {
}
}
/**
+ * Return whether the data network is currently active. An active network means that
+ * it is currently in a high power state for performing data transmission. On some
+ * types of networks, it may be expensive to move and stay in such a state, so it is
+ * more power efficient to batch network traffic together when the radio is already in
+ * this state. This method tells you whether right now is currently a good time to
+ * initiate network traffic, as the network is already active.
+ */
+ public boolean isDefaultNetworkActive() {
+ try {
+ return getNetworkManagementService().isNetworkActive();
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
+ /**
* {@hide}
*/
public ConnectivityManager(IConnectivityManager service, String packageName) {
@@ -977,6 +1502,20 @@
}
/**
+ * Get the set of tethered dhcp ranges.
+ *
+ * @return an array of 0 or more {@code String} of tethered dhcp ranges.
+ * {@hide}
+ */
+ public String[] getTetheredDhcpRanges() {
+ try {
+ return mService.getTetheredDhcpRanges();
+ } catch (RemoteException e) {
+ return new String[0];
+ }
+ }
+
+ /**
* Attempt to tether the named interface. This will setup a dhcp server
* on the interface, forward and NAT IP packets and forward DNS requests
* to the best active upstream network interface. Note that if no upstream
@@ -1020,7 +1559,7 @@
/**
* Check if the device allows for tethering. It may be disabled via
- * {@code ro.tether.denied} system property, {@link Settings#TETHER_SUPPORTED} or
+ * {@code ro.tether.denied} system property, Settings.TETHER_SUPPORTED or
* due to device configuration.
*
* @return a boolean - {@code true} indicating Tethering is supported.
@@ -1163,28 +1702,6 @@
}
/**
- * Try to ensure the device stays awake until we connect with the next network.
- * Actually just holds a wakelock for a number of seconds while we try to connect
- * to any default networks. This will expire if the timeout passes or if we connect
- * to a default after this is called. For internal use only.
- *
- * @param forWhom the name of the network going down for logging purposes
- * @return {@code true} on success, {@code false} on failure
- *
- * <p>This method requires the call to hold the permission
- * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
- * {@hide}
- */
- public boolean requestNetworkTransitionWakelock(String forWhom) {
- try {
- mService.requestNetworkTransitionWakelock(forWhom);
- return true;
- } catch (RemoteException e) {
- return false;
- }
- }
-
- /**
* Report network connectivity status. This is currently used only
* to alter status bar UI.
*
@@ -1203,19 +1720,35 @@
}
/**
+ * Report a problem network to the framework. This provides a hint to the system
+ * that there might be connectivity problems on this network and may cause
+ * the framework to re-evaluate network connectivity and/or switch to another
+ * network.
+ *
+ * @param network The {@link Network} the application was attempting to use
+ * or {@code null} to indicate the current default network.
+ */
+ public void reportBadNetwork(Network network) {
+ try {
+ mService.reportBadNetwork(network);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Set a network-independent global http proxy. This is not normally what you want
* for typical HTTP proxies - they are general network dependent. However if you're
* doing something unusual like general internal filtering this may be useful. On
* a private network where the proxy is not accessible, you may break HTTP using this.
*
- * @param proxyProperties The a {@link ProxyProperites} object defining the new global
+ * @param p The a {@link ProxyInfo} object defining the new global
* HTTP proxy. A {@code null} value will clear the global HTTP proxy.
*
* <p>This method requires the call to hold the permission
- * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
- * {@hide}
+ * android.Manifest.permission#CONNECTIVITY_INTERNAL.
+ * @hide
*/
- public void setGlobalProxy(ProxyProperties p) {
+ public void setGlobalProxy(ProxyInfo p) {
try {
mService.setGlobalProxy(p);
} catch (RemoteException e) {
@@ -1225,14 +1758,14 @@
/**
* Retrieve any network-independent global HTTP proxy.
*
- * @return {@link ProxyProperties} for the current global HTTP proxy or {@code null}
+ * @return {@link ProxyInfo} for the current global HTTP proxy or {@code null}
* if no global HTTP proxy is set.
*
* <p>This method requires the call to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
- * {@hide}
+ * @hide
*/
- public ProxyProperties getGlobalProxy() {
+ public ProxyInfo getGlobalProxy() {
try {
return mService.getGlobalProxy();
} catch (RemoteException e) {
@@ -1244,14 +1777,15 @@
* Get the HTTP proxy settings for the current default network. Note that
* if a global proxy is set, it will override any per-network setting.
*
- * @return the {@link ProxyProperties} for the current HTTP proxy, or {@code null} if no
+ * @return the {@link ProxyInfo} for the current HTTP proxy, or {@code null} if no
* HTTP proxy is active.
*
* <p>This method requires the call to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* {@hide}
+ * @deprecated Deprecated in favor of {@link #getLinkProperties}
*/
- public ProxyProperties getProxy() {
+ public ProxyInfo getProxy() {
try {
return mService.getProxy();
} catch (RemoteException e) {
@@ -1341,24 +1875,6 @@
/**
* Signal that the captive portal check on the indicated network
- * is complete and we can turn the network on for general use.
- *
- * @param info the {@link NetworkInfo} object for the networkType
- * in question.
- *
- * <p>This method requires the call to hold the permission
- * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
- * {@hide}
- */
- public void captivePortalCheckComplete(NetworkInfo info) {
- try {
- mService.captivePortalCheckComplete(info);
- } catch (RemoteException e) {
- }
- }
-
- /**
- * Signal that the captive portal check on the indicated network
* is complete and whether its a captive portal or not.
*
* @param info the {@link NetworkInfo} object for the networkType
@@ -1379,7 +1895,7 @@
/**
* Supply the backend messenger for a network tracker
*
- * @param type NetworkType to set
+ * @param networkType NetworkType to set
* @param messenger {@link Messenger}
* {@hide}
*/
@@ -1503,4 +2019,524 @@
} catch (RemoteException e) {
}
}
+
+ /** {@hide} */
+ public void registerNetworkFactory(Messenger messenger, String name) {
+ try {
+ mService.registerNetworkFactory(messenger, name);
+ } catch (RemoteException e) { }
+ }
+
+ /** {@hide} */
+ public void unregisterNetworkFactory(Messenger messenger) {
+ try {
+ mService.unregisterNetworkFactory(messenger);
+ } catch (RemoteException e) { }
+ }
+
+ /** {@hide} */
+ public void registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
+ NetworkCapabilities nc, int score) {
+ try {
+ mService.registerNetworkAgent(messenger, ni, lp, nc, score);
+ } catch (RemoteException e) { }
+ }
+
+ /**
+ * Base class for NetworkRequest callbacks. Used for notifications about network
+ * changes. Should be extended by applications wanting notifications.
+ */
+ public static class NetworkCallback {
+ /** @hide */
+ public static final int PRECHECK = 1;
+ /** @hide */
+ public static final int AVAILABLE = 2;
+ /** @hide */
+ public static final int LOSING = 3;
+ /** @hide */
+ public static final int LOST = 4;
+ /** @hide */
+ public static final int UNAVAIL = 5;
+ /** @hide */
+ public static final int CAP_CHANGED = 6;
+ /** @hide */
+ public static final int PROP_CHANGED = 7;
+ /** @hide */
+ public static final int CANCELED = 8;
+
+ /**
+ * @hide
+ * Called whenever the framework connects to a network that it may use to
+ * satisfy this request
+ */
+ public void onPreCheck(Network network) {}
+
+ /**
+ * Called when the framework connects and has declared new network ready for use.
+ * This callback may be called more than once if the {@link Network} that is
+ * satisfying the request changes.
+ *
+ * @param network The {@link Network} of the satisfying network.
+ */
+ public void onAvailable(Network network) {}
+
+ /**
+ * Called when the network is about to be disconnected. Often paired with an
+ * {@link NetworkCallback#onAvailable} call with the new replacement network
+ * for graceful handover. This may not be called if we have a hard loss
+ * (loss without warning). This may be followed by either a
+ * {@link NetworkCallback#onLost} call or a
+ * {@link NetworkCallback#onAvailable} call for this network depending
+ * on whether we lose or regain it.
+ *
+ * @param network The {@link Network} that is about to be disconnected.
+ * @param maxMsToLive The time in ms the framework will attempt to keep the
+ * network connected. Note that the network may suffer a
+ * hard loss at any time.
+ */
+ public void onLosing(Network network, int maxMsToLive) {}
+
+ /**
+ * Called when the framework has a hard loss of the network or when the
+ * graceful failure ends.
+ *
+ * @param network The {@link Network} lost.
+ */
+ public void onLost(Network network) {}
+
+ /**
+ * Called if no network is found in the given timeout time. If no timeout is given,
+ * this will not be called.
+ * @hide
+ */
+ public void onUnavailable() {}
+
+ /**
+ * Called when the network the framework connected to for this request
+ * changes capabilities but still satisfies the stated need.
+ *
+ * @param network The {@link Network} whose capabilities have changed.
+ * @param networkCapabilities The new {@link NetworkCapabilities} for this network.
+ */
+ public void onCapabilitiesChanged(Network network,
+ NetworkCapabilities networkCapabilities) {}
+
+ /**
+ * Called when the network the framework connected to for this request
+ * changes {@link LinkProperties}.
+ *
+ * @param network The {@link Network} whose link properties have changed.
+ * @param linkProperties The new {@link LinkProperties} for this network.
+ */
+ public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {}
+
+ private NetworkRequest networkRequest;
+ }
+
+ private static final int BASE = Protocol.BASE_CONNECTIVITY_MANAGER;
+ /** @hide obj = pair(NetworkRequest, Network) */
+ public static final int CALLBACK_PRECHECK = BASE + 1;
+ /** @hide obj = pair(NetworkRequest, Network) */
+ public static final int CALLBACK_AVAILABLE = BASE + 2;
+ /** @hide obj = pair(NetworkRequest, Network), arg1 = ttl */
+ public static final int CALLBACK_LOSING = BASE + 3;
+ /** @hide obj = pair(NetworkRequest, Network) */
+ public static final int CALLBACK_LOST = BASE + 4;
+ /** @hide obj = NetworkRequest */
+ public static final int CALLBACK_UNAVAIL = BASE + 5;
+ /** @hide obj = pair(NetworkRequest, Network) */
+ public static final int CALLBACK_CAP_CHANGED = BASE + 6;
+ /** @hide obj = pair(NetworkRequest, Network) */
+ public static final int CALLBACK_IP_CHANGED = BASE + 7;
+ /** @hide obj = NetworkRequest */
+ public static final int CALLBACK_RELEASED = BASE + 8;
+ /** @hide */
+ public static final int CALLBACK_EXIT = BASE + 9;
+ /** @hide obj = NetworkCapabilities, arg1 = seq number */
+ private static final int EXPIRE_LEGACY_REQUEST = BASE + 10;
+
+ private class CallbackHandler extends Handler {
+ private final HashMap<NetworkRequest, NetworkCallback>mCallbackMap;
+ private final AtomicInteger mRefCount;
+ private static final String TAG = "ConnectivityManager.CallbackHandler";
+ private final ConnectivityManager mCm;
+
+ CallbackHandler(Looper looper, HashMap<NetworkRequest, NetworkCallback>callbackMap,
+ AtomicInteger refCount, ConnectivityManager cm) {
+ super(looper);
+ mCallbackMap = callbackMap;
+ mRefCount = refCount;
+ mCm = cm;
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ Log.d(TAG, "CM callback handler got msg " + message.what);
+ switch (message.what) {
+ case CALLBACK_PRECHECK: {
+ NetworkRequest request = getNetworkRequest(message);
+ NetworkCallback callbacks = getCallbacks(request);
+ if (callbacks != null) {
+ callbacks.onPreCheck(getNetwork(message));
+ } else {
+ Log.e(TAG, "callback not found for PRECHECK message");
+ }
+ break;
+ }
+ case CALLBACK_AVAILABLE: {
+ NetworkRequest request = getNetworkRequest(message);
+ NetworkCallback callbacks = getCallbacks(request);
+ if (callbacks != null) {
+ callbacks.onAvailable(getNetwork(message));
+ } else {
+ Log.e(TAG, "callback not found for AVAILABLE message");
+ }
+ break;
+ }
+ case CALLBACK_LOSING: {
+ NetworkRequest request = getNetworkRequest(message);
+ NetworkCallback callbacks = getCallbacks(request);
+ if (callbacks != null) {
+ callbacks.onLosing(getNetwork(message), message.arg1);
+ } else {
+ Log.e(TAG, "callback not found for LOSING message");
+ }
+ break;
+ }
+ case CALLBACK_LOST: {
+ NetworkRequest request = getNetworkRequest(message);
+ NetworkCallback callbacks = getCallbacks(request);
+ if (callbacks != null) {
+ callbacks.onLost(getNetwork(message));
+ } else {
+ Log.e(TAG, "callback not found for LOST message");
+ }
+ break;
+ }
+ case CALLBACK_UNAVAIL: {
+ NetworkRequest req = (NetworkRequest)message.obj;
+ NetworkCallback callbacks = null;
+ synchronized(mCallbackMap) {
+ callbacks = mCallbackMap.get(req);
+ }
+ if (callbacks != null) {
+ callbacks.onUnavailable();
+ } else {
+ Log.e(TAG, "callback not found for UNAVAIL message");
+ }
+ break;
+ }
+ case CALLBACK_CAP_CHANGED: {
+ NetworkRequest request = getNetworkRequest(message);
+ NetworkCallback callbacks = getCallbacks(request);
+ if (callbacks != null) {
+ Network network = getNetwork(message);
+ NetworkCapabilities cap = mCm.getNetworkCapabilities(network);
+
+ callbacks.onCapabilitiesChanged(network, cap);
+ } else {
+ Log.e(TAG, "callback not found for CHANGED message");
+ }
+ break;
+ }
+ case CALLBACK_IP_CHANGED: {
+ NetworkRequest request = getNetworkRequest(message);
+ NetworkCallback callbacks = getCallbacks(request);
+ if (callbacks != null) {
+ Network network = getNetwork(message);
+ LinkProperties lp = mCm.getLinkProperties(network);
+
+ callbacks.onLinkPropertiesChanged(network, lp);
+ } else {
+ Log.e(TAG, "callback not found for CHANGED message");
+ }
+ break;
+ }
+ case CALLBACK_RELEASED: {
+ NetworkRequest req = (NetworkRequest)message.obj;
+ NetworkCallback callbacks = null;
+ synchronized(mCallbackMap) {
+ callbacks = mCallbackMap.remove(req);
+ }
+ if (callbacks != null) {
+ synchronized(mRefCount) {
+ if (mRefCount.decrementAndGet() == 0) {
+ getLooper().quit();
+ }
+ }
+ } else {
+ Log.e(TAG, "callback not found for CANCELED message");
+ }
+ break;
+ }
+ case CALLBACK_EXIT: {
+ Log.d(TAG, "Listener quiting");
+ getLooper().quit();
+ break;
+ }
+ case EXPIRE_LEGACY_REQUEST: {
+ expireRequest((NetworkCapabilities)message.obj, message.arg1);
+ break;
+ }
+ }
+ }
+
+ private NetworkRequest getNetworkRequest(Message msg) {
+ return (NetworkRequest)(msg.obj);
+ }
+ private NetworkCallback getCallbacks(NetworkRequest req) {
+ synchronized(mCallbackMap) {
+ return mCallbackMap.get(req);
+ }
+ }
+ private Network getNetwork(Message msg) {
+ return new Network(msg.arg2);
+ }
+ private NetworkCallback removeCallbacks(Message msg) {
+ NetworkRequest req = (NetworkRequest)msg.obj;
+ synchronized(mCallbackMap) {
+ return mCallbackMap.remove(req);
+ }
+ }
+ }
+
+ private void incCallbackHandlerRefCount() {
+ synchronized(sCallbackRefCount) {
+ if (sCallbackRefCount.incrementAndGet() == 1) {
+ // TODO - switch this over to a ManagerThread or expire it when done
+ HandlerThread callbackThread = new HandlerThread("ConnectivityManager");
+ callbackThread.start();
+ sCallbackHandler = new CallbackHandler(callbackThread.getLooper(),
+ sNetworkCallback, sCallbackRefCount, this);
+ }
+ }
+ }
+
+ private void decCallbackHandlerRefCount() {
+ synchronized(sCallbackRefCount) {
+ if (sCallbackRefCount.decrementAndGet() == 0) {
+ sCallbackHandler.obtainMessage(CALLBACK_EXIT).sendToTarget();
+ sCallbackHandler = null;
+ }
+ }
+ }
+
+ static final HashMap<NetworkRequest, NetworkCallback> sNetworkCallback =
+ new HashMap<NetworkRequest, NetworkCallback>();
+ static final AtomicInteger sCallbackRefCount = new AtomicInteger(0);
+ static CallbackHandler sCallbackHandler = null;
+
+ private final static int LISTEN = 1;
+ private final static int REQUEST = 2;
+
+ private NetworkRequest sendRequestForNetwork(NetworkCapabilities need,
+ NetworkCallback networkCallback, int timeoutSec, int action,
+ int legacyType) {
+ if (networkCallback == null) {
+ throw new IllegalArgumentException("null NetworkCallback");
+ }
+ if (need == null) throw new IllegalArgumentException("null NetworkCapabilities");
+ try {
+ incCallbackHandlerRefCount();
+ synchronized(sNetworkCallback) {
+ if (action == LISTEN) {
+ networkCallback.networkRequest = mService.listenForNetwork(need,
+ new Messenger(sCallbackHandler), new Binder());
+ } else {
+ networkCallback.networkRequest = mService.requestNetwork(need,
+ new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType);
+ }
+ if (networkCallback.networkRequest != null) {
+ sNetworkCallback.put(networkCallback.networkRequest, networkCallback);
+ }
+ }
+ } catch (RemoteException e) {}
+ if (networkCallback.networkRequest == null) decCallbackHandlerRefCount();
+ return networkCallback.networkRequest;
+ }
+
+ /**
+ * Request a network to satisfy a set of {@link NetworkCapabilities}.
+ *
+ * This {@link NetworkRequest} will live until released via
+ * {@link #unregisterNetworkCallback} or the calling application exits.
+ * Status of the request can be followed by listening to the various
+ * callbacks described in {@link NetworkCallback}. The {@link Network}
+ * can be used to direct traffic to the network.
+ *
+ * @param request {@link NetworkRequest} describing this request.
+ * @param networkCallback The {@link NetworkCallback} to be utilized for this
+ * request. Note the callback must not be shared - they
+ * uniquely specify this request.
+ */
+ public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) {
+ sendRequestForNetwork(request.networkCapabilities, networkCallback, 0,
+ REQUEST, TYPE_NONE);
+ }
+
+ /**
+ * Request a network to satisfy a set of {@link NetworkCapabilities}, limited
+ * by a timeout.
+ *
+ * This function behaves identically to the non-timedout version, but if a suitable
+ * network is not found within the given time (in milliseconds) the
+ * {@link NetworkCallback#unavailable} callback is called. The request must
+ * still be released normally by calling {@link releaseNetworkRequest}.
+ * @param request {@link NetworkRequest} describing this request.
+ * @param networkCallback The callbacks to be utilized for this request. Note
+ * the callbacks must not be shared - they uniquely specify
+ * this request.
+ * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
+ * before {@link NetworkCallback#unavailable} is called.
+ * @hide
+ */
+ public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
+ int timeoutMs) {
+ sendRequestForNetwork(request.networkCapabilities, networkCallback, timeoutMs,
+ REQUEST, TYPE_NONE);
+ }
+
+ /**
+ * The maximum number of milliseconds the framework will look for a suitable network
+ * during a timeout-equiped call to {@link requestNetwork}.
+ * {@hide}
+ */
+ public final static int MAX_NETWORK_REQUEST_TIMEOUT_MS = 100 * 60 * 1000;
+
+ /**
+ * The lookup key for a {@link Network} object included with the intent after
+ * succesfully finding a network for the applications request. Retrieve it with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ * @hide
+ */
+ public static final String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork";
+
+ /**
+ * The lookup key for a {@link NetworkRequest} object included with the intent after
+ * succesfully finding a network for the applications request. Retrieve it with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ * @hide
+ */
+ public static final String EXTRA_NETWORK_REQUEST_NETWORK_REQUEST =
+ "networkRequestNetworkRequest";
+
+
+ /**
+ * Request a network to satisfy a set of {@link NetworkCapabilities}.
+ *
+ * This function behavies identically to the version that takes a NetworkCallback, but instead
+ * of {@link NetworkCallback} a {@link PendingIntent} is used. This means
+ * the request may outlive the calling application and get called back when a suitable
+ * network is found.
+ * <p>
+ * The operation is an Intent broadcast that goes to a broadcast receiver that
+ * you registered with {@link Context#registerReceiver} or through the
+ * <receiver> tag in an AndroidManifest.xml file
+ * <p>
+ * The operation Intent is delivered with two extras, a {@link Network} typed
+ * extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK} and a {@link NetworkRequest}
+ * typed extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK_REQUEST} containing
+ * the original requests parameters. It is important to create a new,
+ * {@link NetworkCallback} based request before completing the processing of the
+ * Intent to reserve the network or it will be released shortly after the Intent
+ * is processed.
+ * <p>
+ * If there is already an request for this Intent registered (with the equality of
+ * two Intents defined by {@link Intent#filterEquals}), then it will be removed and
+ * replaced by this one, effectively releasing the previous {@link NetworkRequest}.
+ * <p>
+ * The request may be released normally by calling {@link #unregisterNetworkCallback}.
+ *
+ * @param request {@link NetworkRequest} describing this request.
+ * @param operation Action to perform when the network is available (corresponds
+ * to the {@link NetworkCallback#onAvailable} call. Typically
+ * comes from {@link PendingIntent#getBroadcast}.
+ * @hide
+ */
+ public void requestNetwork(NetworkRequest request, PendingIntent operation) {
+ try {
+ mService.pendingRequestForNetwork(request.networkCapabilities, operation);
+ } catch (RemoteException e) {}
+ }
+
+ /**
+ * Registers to receive notifications about all networks which satisfy the given
+ * {@link NetworkRequest}. The callbacks will continue to be called until
+ * either the application exits or {@link #unregisterNetworkCallback} is called
+ *
+ * @param request {@link NetworkRequest} describing this request.
+ * @param networkCallback The {@link NetworkCallback} that the system will call as suitable
+ * networks change state.
+ */
+ public void registerNetworkCallback(NetworkRequest request, NetworkCallback networkCallback) {
+ sendRequestForNetwork(request.networkCapabilities, networkCallback, 0, LISTEN, TYPE_NONE);
+ }
+
+ /**
+ * Unregisters callbacks about and possibly releases networks originating from
+ * {@link #requestNetwork} and {@link #registerNetworkCallback} calls. If the
+ * given {@code NetworkCallback} had previosuly been used with {@code #requestNetwork},
+ * any networks that had been connected to only to satisfy that request will be
+ * disconnected.
+ *
+ * @param networkCallback The {@link NetworkCallback} used when making the request.
+ */
+ public void unregisterNetworkCallback(NetworkCallback networkCallback) {
+ if (networkCallback == null || networkCallback.networkRequest == null ||
+ networkCallback.networkRequest.requestId == REQUEST_ID_UNSET) {
+ throw new IllegalArgumentException("Invalid NetworkCallback");
+ }
+ try {
+ mService.releaseNetworkRequest(networkCallback.networkRequest);
+ } catch (RemoteException e) {}
+ }
+
+ /**
+ * Binds the current process to {@code network}. All Sockets created in the future
+ * (and not explicitly bound via a bound SocketFactory from
+ * {@link Network#getSocketFactory() Network.getSocketFactory()}) will be bound to
+ * {@code network}. All host name resolutions will be limited to {@code network} as well.
+ * Note that if {@code network} ever disconnects, all Sockets created in this way will cease to
+ * work and all host name resolutions will fail. This is by design so an application doesn't
+ * accidentally use Sockets it thinks are still bound to a particular {@link Network}.
+ * To clear binding pass {@code null} for {@code network}. Using individually bound
+ * Sockets created by Network.getSocketFactory().createSocket() and
+ * performing network-specific host name resolutions via
+ * {@link Network#getAllByName Network.getAllByName} is preferred to calling
+ * {@code setProcessDefaultNetwork}.
+ *
+ * @param network The {@link Network} to bind the current process to, or {@code null} to clear
+ * the current binding.
+ * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
+ */
+ public static boolean setProcessDefaultNetwork(Network network) {
+ return NetworkUtils.bindProcessToNetwork(network == null ? NETID_UNSET : network.netId);
+ }
+
+ /**
+ * Returns the {@link Network} currently bound to this process via
+ * {@link #setProcessDefaultNetwork}, or {@code null} if no {@link Network} is explicitly bound.
+ *
+ * @return {@code Network} to which this process is bound, or {@code null}.
+ */
+ public static Network getProcessDefaultNetwork() {
+ int netId = NetworkUtils.getNetworkBoundToProcess();
+ if (netId == NETID_UNSET) return null;
+ return new Network(netId);
+ }
+
+ /**
+ * Binds host resolutions performed by this process to {@code network}.
+ * {@link #setProcessDefaultNetwork} takes precedence over this setting.
+ *
+ * @param network The {@link Network} to bind host resolutions from the current process to, or
+ * {@code null} to clear the current binding.
+ * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
+ * @hide
+ * @deprecated This is strictly for legacy usage to support {@link #startUsingNetworkFeature}.
+ */
+ public static boolean setProcessDefaultNetworkForHostResolution(Network network) {
+ return NetworkUtils.bindProcessToNetworkForHostResolution(
+ network == null ? NETID_UNSET : network.netId);
+ }
}
diff --git a/core/java/android/net/DhcpInfo.java b/core/java/android/net/DhcpInfo.java
index 3bede5d..788d7d9 100644
--- a/core/java/android/net/DhcpInfo.java
+++ b/core/java/android/net/DhcpInfo.java
@@ -18,7 +18,6 @@
import android.os.Parcelable;
import android.os.Parcel;
-import java.net.InetAddress;
/**
* A simple object for retrieving the results of a DHCP request.
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 4bca7fe..b9c6491 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -16,12 +16,16 @@
package android.net;
+import android.app.PendingIntent;
import android.net.LinkQualityInfo;
import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkQuotaInfo;
+import android.net.NetworkRequest;
import android.net.NetworkState;
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
import android.os.IBinder;
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
@@ -41,31 +45,28 @@
// Keep this in sync with framework/native/services/connectivitymanager/ConnectivityManager.h
void markSocketAsUser(in ParcelFileDescriptor socket, int uid);
- void setNetworkPreference(int pref);
-
- int getNetworkPreference();
-
NetworkInfo getActiveNetworkInfo();
NetworkInfo getActiveNetworkInfoForUid(int uid);
NetworkInfo getNetworkInfo(int networkType);
+ NetworkInfo getNetworkInfoForNetwork(in Network network);
NetworkInfo[] getAllNetworkInfo();
+ Network[] getAllNetworks();
NetworkInfo getProvisioningOrActiveNetworkInfo();
boolean isNetworkSupported(int networkType);
LinkProperties getActiveLinkProperties();
- LinkProperties getLinkProperties(int networkType);
+ LinkProperties getLinkPropertiesForType(int networkType);
+ LinkProperties getLinkProperties(in Network network);
+
+ NetworkCapabilities getNetworkCapabilities(in Network network);
NetworkState[] getAllNetworkState();
NetworkQuotaInfo getActiveNetworkQuotaInfo();
boolean isActiveNetworkMetered();
- boolean setRadios(boolean onOff);
-
- boolean setRadio(int networkType, boolean turnOn);
-
int startUsingNetworkFeature(int networkType, in String feature,
in IBinder binder);
@@ -75,9 +76,6 @@
boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress, String packageName);
- boolean getMobileDataEnabled();
- void setMobileDataEnabled(boolean enabled);
-
/** Policy control over specific {@link NetworkStateTracker}. */
void setPolicyDataEnable(int networkType, boolean enabled);
@@ -95,6 +93,8 @@
String[] getTetheringErroredIfaces();
+ String[] getTetheredDhcpRanges();
+
String[] getTetherableUsbRegexs();
String[] getTetherableWifiRegexs();
@@ -103,20 +103,18 @@
int setUsbTethering(boolean enable);
- void requestNetworkTransitionWakelock(in String forWhom);
-
void reportInetCondition(int networkType, int percentage);
- ProxyProperties getGlobalProxy();
+ void reportBadNetwork(in Network network);
- void setGlobalProxy(in ProxyProperties p);
+ ProxyInfo getGlobalProxy();
- ProxyProperties getProxy();
+ void setGlobalProxy(in ProxyInfo p);
+
+ ProxyInfo getProxy();
void setDataDependency(int networkType, boolean met);
- boolean protectVpn(in ParcelFileDescriptor socket);
-
boolean prepareVpn(String oldPackage, String newPackage);
ParcelFileDescriptor establishVpn(in VpnConfig config);
@@ -129,8 +127,6 @@
boolean updateLockdownVpn();
- void captivePortalCheckComplete(in NetworkInfo info);
-
void captivePortalCheckCompleted(in NetworkInfo info, boolean isCaptivePortal);
void supplyMessenger(int networkType, in Messenger messenger);
@@ -149,7 +145,31 @@
LinkQualityInfo[] getAllLinkQualityInfo();
- void setProvisioningNotificationVisible(boolean visible, int networkType, in String extraInfo, in String url);
+ void setProvisioningNotificationVisible(boolean visible, int networkType, in String extraInfo,
+ in String url);
void setAirplaneMode(boolean enable);
+
+ void registerNetworkFactory(in Messenger messenger, in String name);
+
+ void unregisterNetworkFactory(in Messenger messenger);
+
+ void registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
+ in NetworkCapabilities nc, int score);
+
+ NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
+ in Messenger messenger, int timeoutSec, in IBinder binder, int legacy);
+
+ NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
+ in PendingIntent operation);
+
+ NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
+ in Messenger messenger, in IBinder binder);
+
+ void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
+ in PendingIntent operation);
+
+ void releaseNetworkRequest(in NetworkRequest networkRequest);
+
+ int getRestoreDefaultNetworkDelay(int networkType);
}
diff --git a/core/java/android/net/IpConfiguration.java b/core/java/android/net/IpConfiguration.java
new file mode 100644
index 0000000..4730bab
--- /dev/null
+++ b/core/java/android/net/IpConfiguration.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2014 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.LinkProperties;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A class representing a configured network.
+ * @hide
+ */
+public class IpConfiguration implements Parcelable {
+ private static final String TAG = "IpConfiguration";
+
+ public enum IpAssignment {
+ /* Use statically configured IP settings. Configuration can be accessed
+ * with linkProperties */
+ STATIC,
+ /* Use dynamically configured IP settigns */
+ DHCP,
+ /* no IP details are assigned, this is used to indicate
+ * that any existing IP settings should be retained */
+ UNASSIGNED
+ }
+
+ public IpAssignment ipAssignment;
+
+ public enum ProxySettings {
+ /* No proxy is to be used. Any existing proxy settings
+ * should be cleared. */
+ NONE,
+ /* Use statically configured proxy. Configuration can be accessed
+ * with linkProperties */
+ STATIC,
+ /* no proxy details are assigned, this is used to indicate
+ * that any existing proxy settings should be retained */
+ UNASSIGNED,
+ /* Use a Pac based proxy.
+ */
+ PAC
+ }
+
+ public ProxySettings proxySettings;
+
+ public LinkProperties linkProperties;
+
+ public IpConfiguration(IpConfiguration source) {
+ if (source != null) {
+ ipAssignment = source.ipAssignment;
+ proxySettings = source.proxySettings;
+ linkProperties = new LinkProperties(source.linkProperties);
+ } else {
+ ipAssignment = IpAssignment.UNASSIGNED;
+ proxySettings = ProxySettings.UNASSIGNED;
+ linkProperties = new LinkProperties();
+ }
+ }
+
+ public IpConfiguration() {
+ this(null);
+ }
+
+ public IpConfiguration(IpAssignment ipAssignment,
+ ProxySettings proxySettings,
+ LinkProperties linkProperties) {
+ this.ipAssignment = ipAssignment;
+ this.proxySettings = proxySettings;
+ this.linkProperties = new LinkProperties(linkProperties);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sbuf = new StringBuilder();
+ sbuf.append("IP assignment: " + ipAssignment.toString());
+ sbuf.append("\n");
+ sbuf.append("Proxy settings: " + proxySettings.toString());
+ sbuf.append("\n");
+ sbuf.append(linkProperties.toString());
+ sbuf.append("\n");
+
+ return sbuf.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof IpConfiguration)) {
+ return false;
+ }
+
+ IpConfiguration other = (IpConfiguration) o;
+ return this.ipAssignment == other.ipAssignment &&
+ this.proxySettings == other.proxySettings &&
+ Objects.equals(this.linkProperties, other.linkProperties);
+ }
+
+ @Override
+ public int hashCode() {
+ return 13 + (linkProperties != null ? linkProperties.hashCode() : 0) +
+ 17 * ipAssignment.ordinal() +
+ 47 * proxySettings.ordinal();
+ }
+
+ /** Implement the Parcelable interface */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(ipAssignment.name());
+ dest.writeString(proxySettings.name());
+ dest.writeParcelable(linkProperties, flags);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<IpConfiguration> CREATOR =
+ new Creator<IpConfiguration>() {
+ public IpConfiguration createFromParcel(Parcel in) {
+ IpConfiguration config = new IpConfiguration();
+ config.ipAssignment = IpAssignment.valueOf(in.readString());
+ config.proxySettings = ProxySettings.valueOf(in.readString());
+ config.linkProperties = in.readParcelable(null);
+ return config;
+ }
+
+ public IpConfiguration[] newArray(int size) {
+ return new IpConfiguration[size];
+ }
+ };
+}
diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java
new file mode 100644
index 0000000..f1fa3eb
--- /dev/null
+++ b/core/java/android/net/IpPrefix.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2014 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.Parcel;
+import android.os.Parcelable;
+import android.util.Pair;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+
+/**
+ * This class represents an IP prefix, i.e., a contiguous block of IP addresses aligned on a
+ * power of two boundary (also known as an "IP subnet"). A prefix is specified by two pieces of
+ * information:
+ *
+ * <ul>
+ * <li>A starting IP address (IPv4 or IPv6). This is the first IP address of the prefix.
+ * <li>A prefix length. This specifies the length of the prefix by specifing the number of bits
+ * in the IP address, starting from the most significant bit in network byte order, that
+ * are constant for all addresses in the prefix.
+ * </ul>
+ *
+ * For example, the prefix <code>192.0.2.0/24</code> covers the 256 IPv4 addresses from
+ * <code>192.0.2.0</code> to <code>192.0.2.255</code>, inclusive, and the prefix
+ * <code>2001:db8:1:2</code> covers the 2^64 IPv6 addresses from <code>2001:db8:1:2::</code> to
+ * <code>2001:db8:1:2:ffff:ffff:ffff:ffff</code>, inclusive.
+ *
+ * Objects of this class are immutable.
+ */
+public final class IpPrefix implements Parcelable {
+ private final byte[] address; // network byte order
+ private final int prefixLength;
+
+ private void checkAndMaskAddressAndPrefixLength() {
+ if (address.length != 4 && address.length != 16) {
+ throw new IllegalArgumentException(
+ "IpPrefix has " + address.length + " bytes which is neither 4 nor 16");
+ }
+ NetworkUtils.maskRawAddress(address, prefixLength);
+ }
+
+ /**
+ * Constructs a new {@code IpPrefix} from a byte array containing an IPv4 or IPv6 address in
+ * network byte order and a prefix length. Silently truncates the address to the prefix length,
+ * so for example {@code 192.0.2.1/24} is silently converted to {@code 192.0.2.0/24}.
+ *
+ * @param address the IP address. Must be non-null and exactly 4 or 16 bytes long.
+ * @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
+ *
+ * @hide
+ */
+ public IpPrefix(byte[] address, int prefixLength) {
+ this.address = address.clone();
+ this.prefixLength = prefixLength;
+ checkAndMaskAddressAndPrefixLength();
+ }
+
+ /**
+ * Constructs a new {@code IpPrefix} from an IPv4 or IPv6 address and a prefix length. Silently
+ * truncates the address to the prefix length, so for example {@code 192.0.2.1/24} is silently
+ * converted to {@code 192.0.2.0/24}.
+ *
+ * @param address the IP address. Must be non-null.
+ * @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
+ * @hide
+ */
+ public IpPrefix(InetAddress address, int prefixLength) {
+ // We don't reuse the (byte[], int) constructor because it calls clone() on the byte array,
+ // which is unnecessary because getAddress() already returns a clone.
+ this.address = address.getAddress();
+ this.prefixLength = prefixLength;
+ checkAndMaskAddressAndPrefixLength();
+ }
+
+ /**
+ * Constructs a new IpPrefix from a string such as "192.0.2.1/24" or "2001:db8::1/64".
+ * Silently truncates the address to the prefix length, so for example {@code 192.0.2.1/24}
+ * is silently converted to {@code 192.0.2.0/24}.
+ *
+ * @param prefix the prefix to parse
+ *
+ * @hide
+ */
+ public IpPrefix(String prefix) {
+ // We don't reuse the (InetAddress, int) constructor because "error: call to this must be
+ // first statement in constructor". We could factor out setting the member variables to an
+ // init() method, but if we did, then we'd have to make the members non-final, or "error:
+ // cannot assign a value to final variable address". So we just duplicate the code here.
+ Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(prefix);
+ this.address = ipAndMask.first.getAddress();
+ this.prefixLength = ipAndMask.second;
+ checkAndMaskAddressAndPrefixLength();
+ }
+
+ /**
+ * Compares this {@code IpPrefix} object against the specified object in {@code obj}. Two
+ * objects are equal if they have the same startAddress and prefixLength.
+ *
+ * @param obj the object to be tested for equality.
+ * @return {@code true} if both objects are equal, {@code false} otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof IpPrefix)) {
+ return false;
+ }
+ IpPrefix that = (IpPrefix) obj;
+ return Arrays.equals(this.address, that.address) && this.prefixLength == that.prefixLength;
+ }
+
+ /**
+ * Gets the hashcode of the represented IP prefix.
+ *
+ * @return the appropriate hashcode value.
+ */
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(address) + 11 * prefixLength;
+ }
+
+ /**
+ * Returns a copy of the first IP address in the prefix. Modifying the returned object does not
+ * change this object's contents.
+ *
+ * @return the address in the form of a byte array.
+ */
+ public InetAddress getAddress() {
+ try {
+ return InetAddress.getByAddress(address);
+ } catch (UnknownHostException e) {
+ // Cannot happen. InetAddress.getByAddress can only throw an exception if the byte
+ // array is the wrong length, but we check that in the constructor.
+ return null;
+ }
+ }
+
+ /**
+ * Returns a copy of the IP address bytes in network order (the highest order byte is the zeroth
+ * element). Modifying the returned array does not change this object's contents.
+ *
+ * @return the address in the form of a byte array.
+ */
+ public byte[] getRawAddress() {
+ return address.clone();
+ }
+
+ /**
+ * Returns the prefix length of this {@code IpPrefix}.
+ *
+ * @return the prefix length.
+ */
+ public int getPrefixLength() {
+ return prefixLength;
+ }
+
+ /**
+ * Returns a string representation of this {@code IpPrefix}.
+ *
+ * @return a string such as {@code "192.0.2.0/24"} or {@code "2001:db8:1:2::"}.
+ */
+ public String toString() {
+ try {
+ return InetAddress.getByAddress(address).getHostAddress() + "/" + prefixLength;
+ } catch(UnknownHostException e) {
+ // Cosmic rays?
+ throw new IllegalStateException("IpPrefix with invalid address! Shouldn't happen.", e);
+ }
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByteArray(address);
+ dest.writeInt(prefixLength);
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public static final Creator<IpPrefix> CREATOR =
+ new Creator<IpPrefix>() {
+ public IpPrefix createFromParcel(Parcel in) {
+ byte[] address = in.createByteArray();
+ int prefixLength = in.readInt();
+ return new IpPrefix(address, prefixLength);
+ }
+
+ public IpPrefix[] newArray(int size) {
+ return new IpPrefix[size];
+ }
+ };
+}
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index a725bec..f9a25f9 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Pair;
import java.net.Inet4Address;
import java.net.InetAddress;
@@ -39,18 +40,13 @@
* <ul>
* <li>An IP address and prefix length (e.g., {@code 2001:db8::1/64} or {@code 192.0.2.1/24}).
* The address must be unicast, as multicast addresses cannot be assigned to interfaces.
- * <li>Address flags: A bitmask of {@code IFA_F_*} values representing properties of the address.
- * <li>Address scope: An integer defining the scope in which the address is unique (e.g.,
- * {@code RT_SCOPE_LINK} or {@code RT_SCOPE_SITE}).
- * <ul>
- *<p>
- * When constructing a {@code LinkAddress}, the IP address and prefix are required. The flags and
- * scope are optional. If they are not specified, the flags are set to zero, and the scope will be
- * determined based on the IP address (e.g., link-local addresses will be created with a scope of
- * {@code RT_SCOPE_LINK}, global addresses with {@code RT_SCOPE_UNIVERSE}, etc.) If they are
- * specified, they are not checked for validity.
- *
- * @hide
+ * <li>Address flags: A bitmask of {@code OsConstants.IFA_F_*} values representing properties
+ * of the address (e.g., {@code android.system.OsConstants.IFA_F_OPTIMISTIC}).
+ * <li>Address scope: One of the {@code OsConstants.IFA_F_*} values; defines the scope in which
+ * the address is unique (e.g.,
+ * {@code android.system.OsConstants.RT_SCOPE_LINK} or
+ * {@code android.system.OsConstants.RT_SCOPE_UNIVERSE}).
+ * </ul>
*/
public class LinkAddress implements Parcelable {
/**
@@ -119,6 +115,10 @@
* the specified flags and scope. Flags and scope are not checked for validity.
* @param address The IP address.
* @param prefixLength The prefix length.
+ * @param flags A bitmask of {@code IFA_F_*} values representing properties of the address.
+ * @param scope An integer defining the scope in which the address is unique (e.g.,
+ * {@link OsConstants#RT_SCOPE_LINK} or {@link OsConstants#RT_SCOPE_SITE}).
+ * @hide
*/
public LinkAddress(InetAddress address, int prefixLength, int flags, int scope) {
init(address, prefixLength, flags, scope);
@@ -129,6 +129,7 @@
* The flags are set to zero and the scope is determined from the address.
* @param address The IP address.
* @param prefixLength The prefix length.
+ * @hide
*/
public LinkAddress(InetAddress address, int prefixLength) {
this(address, prefixLength, 0, 0);
@@ -139,6 +140,7 @@
* Constructs a new {@code LinkAddress} from an {@code InterfaceAddress}.
* The flags are set to zero and the scope is determined from the address.
* @param interfaceAddress The interface address.
+ * @hide
*/
public LinkAddress(InterfaceAddress interfaceAddress) {
this(interfaceAddress.getAddress(),
@@ -149,6 +151,7 @@
* Constructs a new {@code LinkAddress} from a string such as "192.0.2.5/24" or
* "2001:db8::1/64". The flags are set to zero and the scope is determined from the address.
* @param string The string to parse.
+ * @hide
*/
public LinkAddress(String address) {
this(address, 0, 0);
@@ -161,25 +164,12 @@
* @param string The string to parse.
* @param flags The address flags.
* @param scope The address scope.
+ * @hide
*/
public LinkAddress(String address, int flags, int scope) {
- InetAddress inetAddress = null;
- int prefixLength = -1;
- try {
- String [] pieces = address.split("/", 2);
- prefixLength = Integer.parseInt(pieces[1]);
- inetAddress = InetAddress.parseNumericAddress(pieces[0]);
- } catch (NullPointerException e) { // Null string.
- } catch (ArrayIndexOutOfBoundsException e) { // No prefix length.
- } catch (NumberFormatException e) { // Non-numeric prefix.
- } catch (IllegalArgumentException e) { // Invalid IP address.
- }
-
- if (inetAddress == null || prefixLength == -1) {
- throw new IllegalArgumentException("Bad LinkAddress params " + address);
- }
-
- init(inetAddress, prefixLength, flags, scope);
+ // This may throw an IllegalArgumentException; catching it is the caller's responsibility.
+ Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address);
+ init(ipAndMask.first, ipAndMask.second, flags, scope);
}
/**
@@ -194,7 +184,9 @@
/**
* Compares this {@code LinkAddress} instance against {@code obj}. Two addresses are equal if
- * their address, prefix length, flags and scope are equal.
+ * their address, prefix length, flags and scope are equal. Thus, for example, two addresses
+ * that have the same address and prefix length are not equal if one of them is deprecated and
+ * the other is not.
*
* @param obj the object to be tested for equality.
* @return {@code true} if both objects are equal, {@code false} otherwise.
@@ -220,41 +212,52 @@
}
/**
- * Determines whether this {@code LinkAddress} and the provided {@code LinkAddress} represent
- * the same address. Two LinkAddresses represent the same address if they have the same IP
- * address and prefix length, even if their properties are different.
+ * Determines whether this {@code LinkAddress} and the provided {@code LinkAddress}
+ * represent the same address. Two {@code LinkAddresses} represent the same address
+ * if they have the same IP address and prefix length, even if their properties are
+ * different.
*
* @param other the {@code LinkAddress} to compare to.
* @return {@code true} if both objects have the same address and prefix length, {@code false}
* otherwise.
+ * @hide
*/
public boolean isSameAddressAs(LinkAddress other) {
return address.equals(other.address) && prefixLength == other.prefixLength;
}
/**
- * Returns the InetAddress of this address.
+ * Returns the {@link InetAddress} of this {@code LinkAddress}.
*/
public InetAddress getAddress() {
return address;
}
/**
- * Returns the prefix length of this address.
+ * Returns the prefix length of this {@code LinkAddress}.
*/
- public int getNetworkPrefixLength() {
+ public int getPrefixLength() {
return prefixLength;
}
/**
- * Returns the flags of this address.
+ * Returns the prefix length of this {@code LinkAddress}.
+ * TODO: Delete all callers and remove in favour of getPrefixLength().
+ * @hide
+ */
+ public int getNetworkPrefixLength() {
+ return getPrefixLength();
+ }
+
+ /**
+ * Returns the flags of this {@code LinkAddress}.
*/
public int getFlags() {
return flags;
}
/**
- * Returns the scope of this address.
+ * Returns the scope of this {@code LinkAddress}.
*/
public int getScope() {
return scope;
@@ -262,6 +265,7 @@
/**
* Returns true if this {@code LinkAddress} is global scope and preferred.
+ * @hide
*/
public boolean isGlobalPreferred() {
return (scope == RT_SCOPE_UNIVERSE &&
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 4dfd3d9..8be5cf8 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -16,7 +16,7 @@
package android.net;
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
import android.os.Parcelable;
import android.os.Parcel;
import android.text.TextUtils;
@@ -30,42 +30,29 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Hashtable;
+import java.util.List;
+import java.util.Objects;
/**
* Describes the properties of a network link.
*
* A link represents a connection to a network.
* It may have multiple addresses and multiple gateways,
- * multiple dns servers but only one http proxy.
+ * multiple dns servers but only one http proxy and one
+ * network interface.
*
- * Because it's a single network, the dns's
- * are interchangeable and don't need associating with
- * particular addresses. The gateways similarly don't
- * need associating with particular addresses.
+ * Note that this is just a holder of data. Modifying it
+ * does not affect live networks.
*
- * A dual stack interface works fine in this model:
- * each address has it's own prefix length to describe
- * the local network. The dns servers all return
- * both v4 addresses and v6 addresses regardless of the
- * address family of the server itself (rfc4213) and we
- * don't care which is used. The gateways will be
- * selected based on the destination address and the
- * source address has no relavence.
- *
- * Links can also be stacked on top of each other.
- * This can be used, for example, to represent a tunnel
- * interface that runs on top of a physical interface.
- *
- * @hide
*/
-public class LinkProperties implements Parcelable {
+public final class LinkProperties implements Parcelable {
// The interface described by the network link.
private String mIfaceName;
private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>();
private String mDomains;
private ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
- private ProxyProperties mHttpProxy;
+ private ProxyInfo mHttpProxy;
private int mMtu;
// Stores the properties of links that are "stacked" above this link.
@@ -73,9 +60,12 @@
private Hashtable<String, LinkProperties> mStackedLinks =
new Hashtable<String, LinkProperties>();
+ /**
+ * @hide
+ */
public static class CompareResult<T> {
- public Collection<T> removed = new ArrayList<T>();
- public Collection<T> added = new ArrayList<T>();
+ public List<T> removed = new ArrayList<T>();
+ public List<T> added = new ArrayList<T>();
@Override
public String toString() {
@@ -88,20 +78,24 @@
}
}
+ /**
+ * @hide
+ */
public LinkProperties() {
- clear();
}
- // copy constructor instead of clone
+ /**
+ * @hide
+ */
public LinkProperties(LinkProperties source) {
if (source != null) {
mIfaceName = source.getInterfaceName();
for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
- for (InetAddress i : source.getDnses()) mDnses.add(i);
+ for (InetAddress i : source.getDnsServers()) mDnses.add(i);
mDomains = source.getDomains();
for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
mHttpProxy = (source.getHttpProxy() == null) ?
- null : new ProxyProperties(source.getHttpProxy());
+ null : new ProxyInfo(source.getHttpProxy());
for (LinkProperties l: source.mStackedLinks.values()) {
addStackedLink(l);
}
@@ -109,6 +103,13 @@
}
}
+ /**
+ * Sets the interface name for this link. All {@link RouteInfo} already set for this
+ * will have their interface changed to match this new value.
+ *
+ * @param iface The name of the network interface used for this link.
+ * @hide
+ */
public void setInterfaceName(String iface) {
mIfaceName = iface;
ArrayList<RouteInfo> newRoutes = new ArrayList<RouteInfo>(mRoutes.size());
@@ -118,12 +119,20 @@
mRoutes = newRoutes;
}
+ /**
+ * Gets the interface name for this link. May be {@code null} if not set.
+ *
+ * @return The interface name set for this link or {@code null}.
+ */
public String getInterfaceName() {
return mIfaceName;
}
- public Collection<String> getAllInterfaceNames() {
- Collection interfaceNames = new ArrayList<String>(mStackedLinks.size() + 1);
+ /**
+ * @hide
+ */
+ public List<String> getAllInterfaceNames() {
+ List<String> interfaceNames = new ArrayList<String>(mStackedLinks.size() + 1);
if (mIfaceName != null) interfaceNames.add(new String(mIfaceName));
for (LinkProperties stacked: mStackedLinks.values()) {
interfaceNames.addAll(stacked.getAllInterfaceNames());
@@ -132,21 +141,29 @@
}
/**
- * Returns all the addresses on this link.
+ * Returns all the addresses on this link. We often think of a link having a single address,
+ * however, particularly with Ipv6 several addresses are typical. Note that the
+ * {@code LinkProperties} actually contains {@link LinkAddress} objects which also include
+ * prefix lengths for each address. This is a simplified utility alternative to
+ * {@link LinkProperties#getLinkAddresses}.
+ *
+ * @return An umodifiable {@link List} of {@link InetAddress} for this link.
+ * @hide
*/
- public Collection<InetAddress> getAddresses() {
- Collection<InetAddress> addresses = new ArrayList<InetAddress>();
+ public List<InetAddress> getAddresses() {
+ List<InetAddress> addresses = new ArrayList<InetAddress>();
for (LinkAddress linkAddress : mLinkAddresses) {
addresses.add(linkAddress.getAddress());
}
- return Collections.unmodifiableCollection(addresses);
+ return Collections.unmodifiableList(addresses);
}
/**
* Returns all the addresses on this link and all the links stacked above it.
+ * @hide
*/
- public Collection<InetAddress> getAllAddresses() {
- Collection<InetAddress> addresses = new ArrayList<InetAddress>();
+ public List<InetAddress> getAllAddresses() {
+ List<InetAddress> addresses = new ArrayList<InetAddress>();
for (LinkAddress linkAddress : mLinkAddresses) {
addresses.add(linkAddress.getAddress());
}
@@ -166,9 +183,11 @@
}
/**
- * Adds a link address if it does not exist, or updates it if it does.
+ * Adds a {@link LinkAddress} to this {@code LinkProperties} if a {@link LinkAddress} of the
+ * same address/prefix does not already exist. If it does exist it is replaced.
* @param address The {@code LinkAddress} to add.
* @return true if {@code address} was added or updated, false otherwise.
+ * @hide
*/
public boolean addLinkAddress(LinkAddress address) {
if (address == null) {
@@ -190,10 +209,12 @@
}
/**
- * Removes a link address. Specifically, removes the link address, if any, for which
- * {@code isSameAddressAs(toRemove)} returns true.
- * @param address A {@code LinkAddress} specifying the address to remove.
+ * Removes a {@link LinkAddress} from this {@code LinkProperties}. Specifically, matches
+ * and {@link LinkAddress} with the same address and prefix.
+ *
+ * @param toRemove A {@link LinkAddress} specifying the address to remove.
* @return true if the address was removed, false if it did not exist.
+ * @hide
*/
public boolean removeLinkAddress(LinkAddress toRemove) {
int i = findLinkAddressIndex(toRemove);
@@ -205,17 +226,21 @@
}
/**
- * Returns all the addresses on this link.
+ * Returns all the {@link LinkAddress} on this link. Typically a link will have
+ * one IPv4 address and one or more IPv6 addresses.
+ *
+ * @return An unmodifiable {@link List} of {@link LinkAddress} for this link.
*/
- public Collection<LinkAddress> getLinkAddresses() {
- return Collections.unmodifiableCollection(mLinkAddresses);
+ public List<LinkAddress> getLinkAddresses() {
+ return Collections.unmodifiableList(mLinkAddresses);
}
/**
* Returns all the addresses on this link and all the links stacked above it.
+ * @hide
*/
- public Collection<LinkAddress> getAllLinkAddresses() {
- Collection<LinkAddress> addresses = new ArrayList<LinkAddress>();
+ public List<LinkAddress> getAllLinkAddresses() {
+ List<LinkAddress> addresses = new ArrayList<LinkAddress>();
addresses.addAll(mLinkAddresses);
for (LinkProperties stacked: mStackedLinks.values()) {
addresses.addAll(stacked.getAllLinkAddresses());
@@ -224,7 +249,12 @@
}
/**
- * Replaces the LinkAddresses on this link with the given collection of addresses.
+ * Replaces the {@link LinkAddress} in this {@code LinkProperties} with
+ * the given {@link Collection} of {@link LinkAddress}.
+ *
+ * @param addresses The {@link Collection} of {@link LinkAddress} to set in this
+ * object.
+ * @hide
*/
public void setLinkAddresses(Collection<LinkAddress> addresses) {
mLinkAddresses.clear();
@@ -233,26 +263,85 @@
}
}
- public void addDns(InetAddress dns) {
- if (dns != null) mDnses.add(dns);
+ /**
+ * Adds the given {@link InetAddress} to the list of DNS servers, if not present.
+ *
+ * @param dnsServer The {@link InetAddress} to add to the list of DNS servers.
+ * @return true if the DNS server was added, false if it was already present.
+ * @hide
+ */
+ public boolean addDnsServer(InetAddress dnsServer) {
+ if (dnsServer != null && !mDnses.contains(dnsServer)) {
+ mDnses.add(dnsServer);
+ return true;
+ }
+ return false;
}
- public Collection<InetAddress> getDnses() {
- return Collections.unmodifiableCollection(mDnses);
+ /**
+ * Replaces the DNS servers in this {@code LinkProperties} with
+ * the given {@link Collection} of {@link InetAddress} objects.
+ *
+ * @param addresses The {@link Collection} of DNS servers to set in this object.
+ * @hide
+ */
+ public void setDnsServers(Collection<InetAddress> dnsServers) {
+ mDnses.clear();
+ for (InetAddress dnsServer: dnsServers) {
+ addDnsServer(dnsServer);
+ }
}
- public String getDomains() {
- return mDomains;
+ /**
+ * Returns all the {@link InetAddress} for DNS servers on this link.
+ *
+ * @return An umodifiable {@link List} of {@link InetAddress} for DNS servers on
+ * this link.
+ */
+ public List<InetAddress> getDnsServers() {
+ return Collections.unmodifiableList(mDnses);
}
+ /**
+ * Sets the DNS domain search path used on this link.
+ *
+ * @param domains A {@link String} listing in priority order the comma separated
+ * domains to search when resolving host names on this link.
+ * @hide
+ */
public void setDomains(String domains) {
mDomains = domains;
}
+ /**
+ * Get the DNS domains search path set for this link.
+ *
+ * @return A {@link String} containing the comma separated domains to search when resolving
+ * host names on this link.
+ */
+ public String getDomains() {
+ return mDomains;
+ }
+
+ /**
+ * Sets the Maximum Transmission Unit size to use on this link. This should not be used
+ * unless the system default (1500) is incorrect. Values less than 68 or greater than
+ * 10000 will be ignored.
+ *
+ * @param mtu The MTU to use for this link.
+ * @hide
+ */
public void setMtu(int mtu) {
mMtu = mtu;
}
+ /**
+ * Gets any non-default MTU size set for this link. Note that if the default is being used
+ * this will return 0.
+ *
+ * @return The mtu value set for this link.
+ * @hide
+ */
public int getMtu() {
return mMtu;
}
@@ -264,7 +353,18 @@
mIfaceName);
}
- public void addRoute(RouteInfo route) {
+ /**
+ * Adds a {@link RouteInfo} to this {@code LinkProperties}, if not present. If the
+ * {@link RouteInfo} had an interface name set and that differs from the interface set for this
+ * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown. The proper
+ * course is to add either un-named or properly named {@link RouteInfo}.
+ *
+ * @param route A {@link RouteInfo} to add to this object.
+ * @return {@code false} if the route was already present, {@code true} if it was added.
+ *
+ * @hide
+ */
+ public boolean addRoute(RouteInfo route) {
if (route != null) {
String routeIface = route.getInterface();
if (routeIface != null && !routeIface.equals(mIfaceName)) {
@@ -272,22 +372,45 @@
"Route added with non-matching interface: " + routeIface +
" vs. " + mIfaceName);
}
- mRoutes.add(routeWithInterface(route));
+ route = routeWithInterface(route);
+ if (!mRoutes.contains(route)) {
+ mRoutes.add(route);
+ return true;
+ }
}
+ return false;
}
/**
- * Returns all the routes on this link.
+ * Removes a {@link RouteInfo} from this {@code LinkProperties}, if present. The route must
+ * specify an interface and the interface must match the interface of this
+ * {@code LinkProperties}, or it will not be removed.
+ *
+ * @return {@code true} if the route was removed, {@code false} if it was not present.
+ *
+ * @hide
*/
- public Collection<RouteInfo> getRoutes() {
- return Collections.unmodifiableCollection(mRoutes);
+ public boolean removeRoute(RouteInfo route) {
+ return route != null &&
+ Objects.equals(mIfaceName, route.getInterface()) &&
+ mRoutes.remove(route);
+ }
+
+ /**
+ * Returns all the {@link RouteInfo} set on this link.
+ *
+ * @return An unmodifiable {@link List} of {@link RouteInfo} for this link.
+ */
+ public List<RouteInfo> getRoutes() {
+ return Collections.unmodifiableList(mRoutes);
}
/**
* Returns all the routes on this link and all the links stacked above it.
+ * @hide
*/
- public Collection<RouteInfo> getAllRoutes() {
- Collection<RouteInfo> routes = new ArrayList();
+ public List<RouteInfo> getAllRoutes() {
+ List<RouteInfo> routes = new ArrayList();
routes.addAll(mRoutes);
for (LinkProperties stacked: mStackedLinks.values()) {
routes.addAll(stacked.getAllRoutes());
@@ -295,10 +418,24 @@
return routes;
}
- public void setHttpProxy(ProxyProperties proxy) {
+ /**
+ * Sets the recommended {@link ProxyInfo} to use on this link, or {@code null} for none.
+ * Note that Http Proxies are only a hint - the system recommends their use, but it does
+ * not enforce it and applications may ignore them.
+ *
+ * @param proxy A {@link ProxyInfo} defining the Http Proxy to use on this link.
+ * @hide
+ */
+ public void setHttpProxy(ProxyInfo proxy) {
mHttpProxy = proxy;
}
- public ProxyProperties getHttpProxy() {
+
+ /**
+ * Gets the recommended {@link ProxyInfo} (or {@code null}) set on this link.
+ *
+ * @return The {@link ProxyInfo} set on this link
+ */
+ public ProxyInfo getHttpProxy() {
return mHttpProxy;
}
@@ -311,6 +448,7 @@
*
* @param link The link to add.
* @return true if the link was stacked, false otherwise.
+ * @hide
*/
public boolean addStackedLink(LinkProperties link) {
if (link != null && link.getInterfaceName() != null) {
@@ -328,6 +466,7 @@
*
* @param link The link to remove.
* @return true if the link was removed, false otherwise.
+ * @hide
*/
public boolean removeStackedLink(LinkProperties link) {
if (link != null && link.getInterfaceName() != null) {
@@ -339,15 +478,20 @@
/**
* Returns all the links stacked on top of this link.
+ * @hide
*/
- public Collection<LinkProperties> getStackedLinks() {
- Collection<LinkProperties> stacked = new ArrayList<LinkProperties>();
+ public List<LinkProperties> getStackedLinks() {
+ List<LinkProperties> stacked = new ArrayList<LinkProperties>();
for (LinkProperties link : mStackedLinks.values()) {
stacked.add(new LinkProperties(link));
}
- return Collections.unmodifiableCollection(stacked);
+ return Collections.unmodifiableList(stacked);
}
+ /**
+ * Clears this object to its initial state.
+ * @hide
+ */
public void clear() {
mIfaceName = null;
mLinkAddresses.clear();
@@ -361,7 +505,6 @@
/**
* Implement the Parcelable interface
- * @hide
*/
public int describeContents() {
return 0;
@@ -381,12 +524,12 @@
String domainName = "Domains: " + mDomains;
- String mtu = "MTU: " + mMtu;
+ String mtu = " MTU: " + mMtu;
String routes = " Routes: [";
for (RouteInfo route : mRoutes) routes += route.toString() + ",";
routes += "] ";
- String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " ");
+ String proxy = (mHttpProxy == null ? "" : " HttpProxy: " + mHttpProxy.toString() + " ");
String stacked = "";
if (mStackedLinks.values().size() > 0) {
@@ -404,6 +547,7 @@
* Returns true if this link has an IPv4 address.
*
* @return {@code true} if there is an IPv4 address, {@code false} otherwise.
+ * @hide
*/
public boolean hasIPv4Address() {
for (LinkAddress address : mLinkAddresses) {
@@ -415,13 +559,14 @@
}
/**
- * Returns true if this link has an IPv6 address.
+ * Returns true if this link has a global preferred IPv6 address.
*
- * @return {@code true} if there is an IPv6 address, {@code false} otherwise.
+ * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise.
+ * @hide
*/
- public boolean hasIPv6Address() {
+ public boolean hasGlobalIPv6Address() {
for (LinkAddress address : mLinkAddresses) {
- if (address.getAddress() instanceof Inet6Address) {
+ if (address.getAddress() instanceof Inet6Address && address.isGlobalPreferred()) {
return true;
}
}
@@ -429,10 +574,85 @@
}
/**
+ * Returns true if this link has an IPv4 default route.
+ *
+ * @return {@code true} if there is an IPv4 default route, {@code false} otherwise.
+ * @hide
+ */
+ public boolean hasIPv4DefaultRoute() {
+ for (RouteInfo r : mRoutes) {
+ if (r.isIPv4Default()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this link has an IPv6 default route.
+ *
+ * @return {@code true} if there is an IPv6 default route, {@code false} otherwise.
+ * @hide
+ */
+ public boolean hasIPv6DefaultRoute() {
+ for (RouteInfo r : mRoutes) {
+ if (r.isIPv6Default()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this link has an IPv4 DNS server.
+ *
+ * @return {@code true} if there is an IPv4 DNS server, {@code false} otherwise.
+ * @hide
+ */
+ public boolean hasIPv4DnsServer() {
+ for (InetAddress ia : mDnses) {
+ if (ia instanceof Inet4Address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this link has an IPv6 DNS server.
+ *
+ * @return {@code true} if there is an IPv6 DNS server, {@code false} otherwise.
+ * @hide
+ */
+ public boolean hasIPv6DnsServer() {
+ for (InetAddress ia : mDnses) {
+ if (ia instanceof Inet6Address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this link is provisioned for global connectivity. For IPv6, this requires an
+ * IP address, default route, and DNS server. For IPv4, this requires only an IPv4 address,
+ * because WifiStateMachine accepts static configurations that only specify an address but not
+ * DNS servers or a default route.
+ *
+ * @return {@code true} if the link is provisioned, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isProvisioned() {
+ return (hasIPv4Address() ||
+ (hasGlobalIPv6Address() && hasIPv6DefaultRoute() && hasIPv6DnsServer()));
+ }
+
+ /**
* Compares this {@code LinkProperties} interface name against the target
*
* @param target LinkProperties to compare.
* @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
*/
public boolean isIdenticalInterfaceName(LinkProperties target) {
return TextUtils.equals(getInterfaceName(), target.getInterfaceName());
@@ -443,6 +663,7 @@
*
* @param target LinkProperties to compare.
* @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
*/
public boolean isIdenticalAddresses(LinkProperties target) {
Collection<InetAddress> targetAddresses = target.getAddresses();
@@ -456,9 +677,10 @@
*
* @param target LinkProperties to compare.
* @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
*/
public boolean isIdenticalDnses(LinkProperties target) {
- Collection<InetAddress> targetDnses = target.getDnses();
+ Collection<InetAddress> targetDnses = target.getDnsServers();
String targetDomains = target.getDomains();
if (mDomains == null) {
if (targetDomains != null) return false;
@@ -474,6 +696,7 @@
*
* @param target LinkProperties to compare.
* @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
*/
public boolean isIdenticalRoutes(LinkProperties target) {
Collection<RouteInfo> targetRoutes = target.getRoutes();
@@ -486,6 +709,7 @@
*
* @param target LinkProperties to compare.
* @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
*/
public boolean isIdenticalHttpProxy(LinkProperties target) {
return getHttpProxy() == null ? target.getHttpProxy() == null :
@@ -497,6 +721,7 @@
*
* @param target LinkProperties to compare.
* @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
*/
public boolean isIdenticalStackedLinks(LinkProperties target) {
if (!mStackedLinks.keySet().equals(target.mStackedLinks.keySet())) {
@@ -517,6 +742,7 @@
*
* @param target LinkProperties to compare.
* @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
*/
public boolean isIdenticalMtu(LinkProperties target) {
return getMtu() == target.getMtu();
@@ -534,10 +760,6 @@
* 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal.
* 2. Worst case performance is O(n^2).
*
- * This method does not check that stacked interfaces are equal, because
- * stacked interfaces are not so much a property of the link as a
- * description of connections between links.
- *
* @param obj the object to be tested for equality.
* @return {@code true} if both objects are equal, {@code false} otherwise.
*/
@@ -547,7 +769,11 @@
if (!(obj instanceof LinkProperties)) return false;
LinkProperties target = (LinkProperties) obj;
-
+ /**
+ * This method does not check that stacked interfaces are equal, because
+ * stacked interfaces are not so much a property of the link as a
+ * description of connections between links.
+ */
return isIdenticalInterfaceName(target) &&
isIdenticalAddresses(target) &&
isIdenticalDnses(target) &&
@@ -563,6 +789,7 @@
*
* @param target a LinkProperties with the new list of addresses
* @return the differences between the addresses.
+ * @hide
*/
public CompareResult<LinkAddress> compareAddresses(LinkProperties target) {
/*
@@ -591,6 +818,7 @@
*
* @param target a LinkProperties with the new list of dns addresses
* @return the differences between the DNS addresses.
+ * @hide
*/
public CompareResult<InetAddress> compareDnses(LinkProperties target) {
/*
@@ -605,7 +833,7 @@
result.removed = new ArrayList<InetAddress>(mDnses);
result.added.clear();
if (target != null) {
- for (InetAddress newAddress : target.getDnses()) {
+ for (InetAddress newAddress : target.getDnsServers()) {
if (! result.removed.remove(newAddress)) {
result.added.add(newAddress);
}
@@ -620,6 +848,7 @@
*
* @param target a LinkProperties with the new list of routes
* @return the differences between the routes.
+ * @hide
*/
public CompareResult<RouteInfo> compareAllRoutes(LinkProperties target) {
/*
@@ -642,6 +871,35 @@
return result;
}
+ /**
+ * Compares all interface names in this LinkProperties with another
+ * LinkProperties, examining both the the base link and all stacked links.
+ *
+ * @param target a LinkProperties with the new list of interface names
+ * @return the differences between the interface names.
+ * @hide
+ */
+ public CompareResult<String> compareAllInterfaceNames(LinkProperties target) {
+ /*
+ * Duplicate the interface names into removed, we will be removing
+ * interface names which are common between this and target
+ * leaving the interface names that are different. And interface names which
+ * are in target but not in this are placed in added.
+ */
+ CompareResult<String> result = new CompareResult<String>();
+
+ result.removed = getAllInterfaceNames();
+ result.added.clear();
+ if (target != null) {
+ for (String r : target.getAllInterfaceNames()) {
+ if (! result.removed.remove(r)) {
+ result.added.add(r);
+ }
+ }
+ }
+ return result;
+ }
+
@Override
/**
@@ -710,7 +968,7 @@
addressCount = in.readInt();
for (int i=0; i<addressCount; i++) {
try {
- netProp.addDns(InetAddress.getByAddress(in.createByteArray()));
+ netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray()));
} catch (UnknownHostException e) { }
}
netProp.setDomains(in.readString());
@@ -720,7 +978,7 @@
netProp.addRoute((RouteInfo)in.readParcelable(null));
}
if (in.readByte() == 1) {
- netProp.setHttpProxy((ProxyProperties)in.readParcelable(null));
+ netProp.setHttpProxy((ProxyInfo)in.readParcelable(null));
}
ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
in.readList(stackedLinks, LinkProperties.class.getClassLoader());
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
new file mode 100644
index 0000000..9a22d78
--- /dev/null
+++ b/core/java/android/net/Network.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2014 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.NetworkUtils;
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import javax.net.SocketFactory;
+
+/**
+ * Identifies a {@code Network}. This is supplied to applications via
+ * {@link ConnectivityManager.NetworkCallback} in response to the active
+ * {@link ConnectivityManager#requestNetwork} or passive
+ * {@link ConnectivityManager#registerNetworkCallback} calls.
+ * It is used to direct traffic to the given {@code Network}, either on a {@link Socket} basis
+ * through a targeted {@link SocketFactory} or process-wide via
+ * {@link ConnectivityManager#setProcessDefaultNetwork}.
+ */
+public class Network implements Parcelable {
+
+ /**
+ * @hide
+ */
+ public final int netId;
+
+ private NetworkBoundSocketFactory mNetworkBoundSocketFactory = null;
+
+ /**
+ * @hide
+ */
+ public Network(int netId) {
+ this.netId = netId;
+ }
+
+ /**
+ * @hide
+ */
+ public Network(Network that) {
+ this.netId = that.netId;
+ }
+
+ /**
+ * Operates the same as {@code InetAddress.getAllByName} except that host
+ * resolution is done on this network.
+ *
+ * @param host the hostname or literal IP string to be resolved.
+ * @return the array of addresses associated with the specified host.
+ * @throws UnknownHostException if the address lookup fails.
+ */
+ public InetAddress[] getAllByName(String host) throws UnknownHostException {
+ return InetAddress.getAllByNameOnNet(host, netId);
+ }
+
+ /**
+ * Operates the same as {@code InetAddress.getByName} except that host
+ * resolution is done on this network.
+ *
+ * @param host
+ * the hostName to be resolved to an address or {@code null}.
+ * @return the {@code InetAddress} instance representing the host.
+ * @throws UnknownHostException
+ * if the address lookup fails.
+ */
+ public InetAddress getByName(String host) throws UnknownHostException {
+ return InetAddress.getByNameOnNet(host, netId);
+ }
+
+ /**
+ * A {@code SocketFactory} that produces {@code Socket}'s bound to this network.
+ */
+ private class NetworkBoundSocketFactory extends SocketFactory {
+ private final int mNetId;
+
+ public NetworkBoundSocketFactory(int netId) {
+ super();
+ mNetId = netId;
+ }
+
+ private void connectToHost(Socket socket, String host, int port) throws IOException {
+ // Lookup addresses only on this Network.
+ InetAddress[] hostAddresses = getAllByName(host);
+ // Try all but last address ignoring exceptions.
+ for (int i = 0; i < hostAddresses.length - 1; i++) {
+ try {
+ socket.connect(new InetSocketAddress(hostAddresses[i], port));
+ return;
+ } catch (IOException e) {
+ }
+ }
+ // Try last address. Do throw exceptions.
+ socket.connect(new InetSocketAddress(hostAddresses[hostAddresses.length - 1], port));
+ }
+
+ @Override
+ public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
+ Socket socket = createSocket();
+ socket.bind(new InetSocketAddress(localHost, localPort));
+ connectToHost(socket, host, port);
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
+ int localPort) throws IOException {
+ Socket socket = createSocket();
+ socket.bind(new InetSocketAddress(localAddress, localPort));
+ socket.connect(new InetSocketAddress(address, port));
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(InetAddress host, int port) throws IOException {
+ Socket socket = createSocket();
+ socket.connect(new InetSocketAddress(host, port));
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(String host, int port) throws IOException {
+ Socket socket = createSocket();
+ connectToHost(socket, host, port);
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket() throws IOException {
+ Socket socket = new Socket();
+ // Query a property of the underlying socket to ensure the underlying
+ // socket exists so a file descriptor is available to bind to a network.
+ socket.getReuseAddress();
+ if (!NetworkUtils.bindSocketToNetwork(socket.getFileDescriptor$().getInt$(), mNetId)) {
+ throw new SocketException("Failed to bind socket to network.");
+ }
+ return socket;
+ }
+ }
+
+ /**
+ * Returns a {@link SocketFactory} bound to this network. Any {@link Socket} created by
+ * this factory will have its traffic sent over this {@code Network}. Note that if this
+ * {@code Network} ever disconnects, this factory and any {@link Socket} it produced in the
+ * past or future will cease to work.
+ *
+ * @return a {@link SocketFactory} which produces {@link Socket} instances bound to this
+ * {@code Network}.
+ */
+ public SocketFactory getSocketFactory() {
+ if (mNetworkBoundSocketFactory == null) {
+ mNetworkBoundSocketFactory = new NetworkBoundSocketFactory(netId);
+ }
+ return mNetworkBoundSocketFactory;
+ }
+
+ // implement the Parcelable interface
+ public int describeContents() {
+ return 0;
+ }
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(netId);
+ }
+
+ public static final Creator<Network> CREATOR =
+ new Creator<Network>() {
+ public Network createFromParcel(Parcel in) {
+ int netId = in.readInt();
+
+ return new Network(netId);
+ }
+
+ public Network[] newArray(int size) {
+ return new Network[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Network == false) return false;
+ Network other = (Network)obj;
+ return this.netId == other.netId;
+ }
+
+ @Override
+ public int hashCode() {
+ return netId * 11;
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(netId);
+ }
+}
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
new file mode 100644
index 0000000..41eab02
--- /dev/null
+++ b/core/java/android/net/NetworkAgent.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2014 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.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A Utility class for handling for communicating between bearer-specific
+ * code and ConnectivityService.
+ *
+ * A bearer may have more than one NetworkAgent if it can simultaneously
+ * support separate networks (IMS / Internet / MMS Apns on cellular, or
+ * perhaps connections with different SSID or P2P for Wi-Fi).
+ *
+ * @hide
+ */
+public abstract class NetworkAgent extends Handler {
+ private volatile AsyncChannel mAsyncChannel;
+ private final String LOG_TAG;
+ private static final boolean DBG = true;
+ private static final boolean VDBG = true;
+ private final Context mContext;
+ private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();
+
+ private static final int BASE = Protocol.BASE_NETWORK_AGENT;
+
+ /**
+ * Sent by ConnectivityService to the NetworkAgent to inform it of
+ * suspected connectivity problems on its network. The NetworkAgent
+ * should take steps to verify and correct connectivity.
+ */
+ public static final int CMD_SUSPECT_BAD = BASE;
+
+ /**
+ * Sent by the NetworkAgent (note the EVENT vs CMD prefix) to
+ * ConnectivityService to pass the current NetworkInfo (connection state).
+ * Sent when the NetworkInfo changes, mainly due to change of state.
+ * obj = NetworkInfo
+ */
+ public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 1;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to pass the current
+ * NetworkCapabilties.
+ * obj = NetworkCapabilities
+ */
+ public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 2;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to pass the current
+ * NetworkProperties.
+ * obj = NetworkProperties
+ */
+ public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3;
+
+ /* centralize place where base network score, and network score scaling, will be
+ * stored, so as we can consistently compare apple and oranges, or wifi, ethernet and LTE
+ */
+ public static final int WIFI_BASE_SCORE = 60;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to pass the current
+ * network score.
+ * obj = network score Integer
+ */
+ public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to add new UID ranges
+ * to be forced into this Network. For VPNs only.
+ * obj = UidRange[] to forward
+ */
+ public static final int EVENT_UID_RANGES_ADDED = BASE + 5;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to remove UID ranges
+ * from being forced into this Network. For VPNs only.
+ * obj = UidRange[] to stop forwarding
+ */
+ public static final int EVENT_UID_RANGES_REMOVED = BASE + 6;
+
+ public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
+ NetworkCapabilities nc, LinkProperties lp, int score) {
+ super(looper);
+ LOG_TAG = logTag;
+ mContext = context;
+ if (ni == null || nc == null || lp == null) {
+ throw new IllegalArgumentException();
+ }
+
+ if (DBG) log("Registering NetworkAgent");
+ ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
+ new LinkProperties(lp), new NetworkCapabilities(nc), score);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
+ if (mAsyncChannel != null) {
+ log("Received new connection while already connected!");
+ } else {
+ if (DBG) log("NetworkAgent fully connected");
+ AsyncChannel ac = new AsyncChannel();
+ ac.connected(null, this, msg.replyTo);
+ ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+ AsyncChannel.STATUS_SUCCESSFUL);
+ synchronized (mPreConnectedQueue) {
+ mAsyncChannel = ac;
+ for (Message m : mPreConnectedQueue) {
+ ac.sendMessage(m);
+ }
+ mPreConnectedQueue.clear();
+ }
+ }
+ break;
+ }
+ case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
+ if (DBG) log("CMD_CHANNEL_DISCONNECT");
+ if (mAsyncChannel != null) mAsyncChannel.disconnect();
+ break;
+ }
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+ if (DBG) log("NetworkAgent channel lost");
+ // let the client know CS is done with us.
+ unwanted();
+ synchronized (mPreConnectedQueue) {
+ mAsyncChannel = null;
+ }
+ break;
+ }
+ case CMD_SUSPECT_BAD: {
+ log("Unhandled Message " + msg);
+ break;
+ }
+ }
+ }
+
+ private void queueOrSendMessage(int what, Object obj) {
+ synchronized (mPreConnectedQueue) {
+ if (mAsyncChannel != null) {
+ mAsyncChannel.sendMessage(what, obj);
+ } else {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.obj = obj;
+ mPreConnectedQueue.add(msg);
+ }
+ }
+ }
+
+ /**
+ * Called by the bearer code when it has new LinkProperties data.
+ */
+ public void sendLinkProperties(LinkProperties linkProperties) {
+ queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
+ }
+
+ /**
+ * Called by the bearer code when it has new NetworkInfo data.
+ */
+ public void sendNetworkInfo(NetworkInfo networkInfo) {
+ queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
+ }
+
+ /**
+ * Called by the bearer code when it has new NetworkCapabilities data.
+ */
+ public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) {
+ queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
+ new NetworkCapabilities(networkCapabilities));
+ }
+
+ /**
+ * Called by the bearer code when it has a new score for this network.
+ */
+ public void sendNetworkScore(int score) {
+ queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new Integer(score));
+ }
+
+ /**
+ * Called by the VPN code when it wants to add ranges of UIDs to be routed
+ * through the VPN network.
+ */
+ public void addUidRanges(UidRange[] ranges) {
+ queueOrSendMessage(EVENT_UID_RANGES_ADDED, ranges);
+ }
+
+ /**
+ * Called by the VPN code when it wants to remove ranges of UIDs from being routed
+ * through the VPN network.
+ */
+ public void removeUidRanges(UidRange[] ranges) {
+ queueOrSendMessage(EVENT_UID_RANGES_REMOVED, ranges);
+ }
+
+ /**
+ * Called when ConnectivityService has indicated they no longer want this network.
+ * The parent factory should (previously) have received indication of the change
+ * as well, either canceling NetworkRequests or altering their score such that this
+ * network won't be immediately requested again.
+ */
+ abstract protected void unwanted();
+
+ protected void log(String s) {
+ Log.d(LOG_TAG, "NetworkAgent: " + s);
+ }
+}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
new file mode 100644
index 0000000..53f9fcd
--- /dev/null
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -0,0 +1,619 @@
+/*
+ * Copyright (C) 2014 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.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.lang.IllegalArgumentException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * This class represents the capabilities of a network. This is used both to specify
+ * needs to {@link ConnectivityManager} and when inspecting a network.
+ *
+ * Note that this replaces the old {@link ConnectivityManager#TYPE_MOBILE} method
+ * of network selection. Rather than indicate a need for Wi-Fi because an application
+ * needs high bandwidth and risk obselence when a new, fast network appears (like LTE),
+ * the application should specify it needs high bandwidth. Similarly if an application
+ * needs an unmetered network for a bulk transfer it can specify that rather than assuming
+ * all cellular based connections are metered and all Wi-Fi based connections are not.
+ */
+public final class NetworkCapabilities implements Parcelable {
+ private static final String TAG = "NetworkCapabilities";
+ private static final boolean DBG = false;
+
+ /**
+ * @hide
+ */
+ public NetworkCapabilities() {
+ }
+
+ public NetworkCapabilities(NetworkCapabilities nc) {
+ if (nc != null) {
+ mNetworkCapabilities = nc.mNetworkCapabilities;
+ mTransportTypes = nc.mTransportTypes;
+ mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
+ mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
+ mNetworkSpecifier = nc.mNetworkSpecifier;
+ }
+ }
+
+ /**
+ * Represents the network's capabilities. If any are specified they will be satisfied
+ * by any Network that matches all of them.
+ */
+ private long mNetworkCapabilities = (1 << NET_CAPABILITY_NOT_RESTRICTED) |
+ (1 << NET_CAPABILITY_TRUSTED) | (1 << NET_CAPABILITY_NOT_VPN);
+
+ /**
+ * Indicates this is a network that has the ability to reach the
+ * carrier's MMSC for sending and receiving MMS messages.
+ */
+ public static final int NET_CAPABILITY_MMS = 0;
+
+ /**
+ * Indicates this is a network that has the ability to reach the carrier's
+ * SUPL server, used to retrieve GPS information.
+ */
+ public static final int NET_CAPABILITY_SUPL = 1;
+
+ /**
+ * Indicates this is a network that has the ability to reach the carrier's
+ * DUN or tethering gateway.
+ */
+ public static final int NET_CAPABILITY_DUN = 2;
+
+ /**
+ * Indicates this is a network that has the ability to reach the carrier's
+ * FOTA portal, used for over the air updates.
+ */
+ public static final int NET_CAPABILITY_FOTA = 3;
+
+ /**
+ * Indicates this is a network that has the ability to reach the carrier's
+ * IMS servers, used for network registration and signaling.
+ */
+ public static final int NET_CAPABILITY_IMS = 4;
+
+ /**
+ * Indicates this is a network that has the ability to reach the carrier's
+ * CBS servers, used for carrier specific services.
+ */
+ public static final int NET_CAPABILITY_CBS = 5;
+
+ /**
+ * Indicates this is a network that has the ability to reach a Wi-Fi direct
+ * peer.
+ */
+ public static final int NET_CAPABILITY_WIFI_P2P = 6;
+
+ /**
+ * Indicates this is a network that has the ability to reach a carrier's
+ * Initial Attach servers.
+ */
+ public static final int NET_CAPABILITY_IA = 7;
+
+ /**
+ * Indicates this is a network that has the ability to reach a carrier's
+ * RCS servers, used for Rich Communication Services.
+ */
+ public static final int NET_CAPABILITY_RCS = 8;
+
+ /**
+ * Indicates this is a network that has the ability to reach a carrier's
+ * XCAP servers, used for configuration and control.
+ */
+ public static final int NET_CAPABILITY_XCAP = 9;
+
+ /**
+ * Indicates this is a network that has the ability to reach a carrier's
+ * Emergency IMS servers, used for network signaling during emergency calls.
+ */
+ public static final int NET_CAPABILITY_EIMS = 10;
+
+ /**
+ * Indicates that this network is unmetered.
+ */
+ public static final int NET_CAPABILITY_NOT_METERED = 11;
+
+ /**
+ * Indicates that this network should be able to reach the internet.
+ */
+ public static final int NET_CAPABILITY_INTERNET = 12;
+
+ /**
+ * Indicates that this network is available for general use. If this is not set
+ * applications should not attempt to communicate on this network. Note that this
+ * is simply informative and not enforcement - enforcement is handled via other means.
+ * Set by default.
+ */
+ public static final int NET_CAPABILITY_NOT_RESTRICTED = 13;
+
+ /**
+ * Indicates that the user has indicated implicit trust of this network. This
+ * generally means it's a sim-selected carrier, a plugged in ethernet, a paired
+ * BT device or a wifi the user asked to connect to. Untrusted networks
+ * are probably limited to unknown wifi AP. Set by default.
+ */
+ public static final int NET_CAPABILITY_TRUSTED = 14;
+
+ /*
+ * Indicates that this network is not a VPN. This capability is set by default and should be
+ * explicitly cleared when creating VPN networks.
+ */
+ public static final int NET_CAPABILITY_NOT_VPN = 15;
+
+
+ private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VPN;
+
+ /**
+ * Adds the given capability to this {@code NetworkCapability} instance.
+ * Multiple capabilities may be applied sequentially. Note that when searching
+ * for a network to satisfy a request, all capabilities requested must be satisfied.
+ *
+ * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be added.
+ * @return This NetworkCapability to facilitate chaining.
+ * @hide
+ */
+ public NetworkCapabilities addCapability(int capability) {
+ if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
+ throw new IllegalArgumentException("NetworkCapability out of range");
+ }
+ mNetworkCapabilities |= 1 << capability;
+ return this;
+ }
+
+ /**
+ * Removes (if found) the given capability from this {@code NetworkCapability} instance.
+ *
+ * @param capability the {@code NetworkCapabilities.NET_CAPABILTIY_*} to be removed.
+ * @return This NetworkCapability to facilitate chaining.
+ * @hide
+ */
+ public NetworkCapabilities removeCapability(int capability) {
+ if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
+ throw new IllegalArgumentException("NetworkCapability out of range");
+ }
+ mNetworkCapabilities &= ~(1 << capability);
+ return this;
+ }
+
+ /**
+ * Gets all the capabilities set on this {@code NetworkCapability} instance.
+ *
+ * @return an array of {@code NetworkCapabilities.NET_CAPABILITY_*} values
+ * for this instance.
+ * @hide
+ */
+ public int[] getCapabilities() {
+ return enumerateBits(mNetworkCapabilities);
+ }
+
+ /**
+ * Tests for the presence of a capabilitity on this instance.
+ *
+ * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be tested for.
+ * @return {@code true} if set on this instance.
+ */
+ public boolean hasCapability(int capability) {
+ if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
+ return false;
+ }
+ return ((mNetworkCapabilities & (1 << capability)) != 0);
+ }
+
+ private int[] enumerateBits(long val) {
+ int size = Long.bitCount(val);
+ int[] result = new int[size];
+ int index = 0;
+ int resource = 0;
+ while (val > 0) {
+ if ((val & 1) == 1) result[index++] = resource;
+ val = val >> 1;
+ resource++;
+ }
+ return result;
+ }
+
+ private void combineNetCapabilities(NetworkCapabilities nc) {
+ this.mNetworkCapabilities |= nc.mNetworkCapabilities;
+ }
+
+ private boolean satisfiedByNetCapabilities(NetworkCapabilities nc) {
+ return ((nc.mNetworkCapabilities & this.mNetworkCapabilities) == this.mNetworkCapabilities);
+ }
+
+ private boolean equalsNetCapabilities(NetworkCapabilities nc) {
+ return (nc.mNetworkCapabilities == this.mNetworkCapabilities);
+ }
+
+ /**
+ * Representing the transport type. Apps should generally not care about transport. A
+ * request for a fast internet connection could be satisfied by a number of different
+ * transports. If any are specified here it will be satisfied a Network that matches
+ * any of them. If a caller doesn't care about the transport it should not specify any.
+ */
+ private long mTransportTypes;
+
+ /**
+ * Indicates this network uses a Cellular transport.
+ */
+ public static final int TRANSPORT_CELLULAR = 0;
+
+ /**
+ * Indicates this network uses a Wi-Fi transport.
+ */
+ public static final int TRANSPORT_WIFI = 1;
+
+ /**
+ * Indicates this network uses a Bluetooth transport.
+ */
+ public static final int TRANSPORT_BLUETOOTH = 2;
+
+ /**
+ * Indicates this network uses an Ethernet transport.
+ */
+ public static final int TRANSPORT_ETHERNET = 3;
+
+ /**
+ * Indicates this network uses a VPN transport.
+ */
+ public static final int TRANSPORT_VPN = 4;
+
+ private static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
+ private static final int MAX_TRANSPORT = TRANSPORT_VPN;
+
+ /**
+ * Adds the given transport type to this {@code NetworkCapability} instance.
+ * Multiple transports may be applied sequentially. Note that when searching
+ * for a network to satisfy a request, any listed in the request will satisfy the request.
+ * For example {@code TRANSPORT_WIFI} and {@code TRANSPORT_ETHERNET} added to a
+ * {@code NetworkCapabilities} would cause either a Wi-Fi network or an Ethernet network
+ * to be selected. This is logically different than
+ * {@code NetworkCapabilities.NET_CAPABILITY_*} listed above.
+ *
+ * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be added.
+ * @return This NetworkCapability to facilitate chaining.
+ * @hide
+ */
+ public NetworkCapabilities addTransportType(int transportType) {
+ if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
+ throw new IllegalArgumentException("TransportType out of range");
+ }
+ mTransportTypes |= 1 << transportType;
+ setNetworkSpecifier(mNetworkSpecifier); // used for exception checking
+ return this;
+ }
+
+ /**
+ * Removes (if found) the given transport from this {@code NetworkCapability} instance.
+ *
+ * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be removed.
+ * @return This NetworkCapability to facilitate chaining.
+ * @hide
+ */
+ public NetworkCapabilities removeTransportType(int transportType) {
+ if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
+ throw new IllegalArgumentException("TransportType out of range");
+ }
+ mTransportTypes &= ~(1 << transportType);
+ setNetworkSpecifier(mNetworkSpecifier); // used for exception checking
+ return this;
+ }
+
+ /**
+ * Gets all the transports set on this {@code NetworkCapability} instance.
+ *
+ * @return an array of {@code NetworkCapabilities.TRANSPORT_*} values
+ * for this instance.
+ * @hide
+ */
+ public int[] getTransportTypes() {
+ return enumerateBits(mTransportTypes);
+ }
+
+ /**
+ * Tests for the presence of a transport on this instance.
+ *
+ * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be tested for.
+ * @return {@code true} if set on this instance.
+ */
+ public boolean hasTransport(int transportType) {
+ if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
+ return false;
+ }
+ return ((mTransportTypes & (1 << transportType)) != 0);
+ }
+
+ private void combineTransportTypes(NetworkCapabilities nc) {
+ this.mTransportTypes |= nc.mTransportTypes;
+ }
+ private boolean satisfiedByTransportTypes(NetworkCapabilities nc) {
+ return ((this.mTransportTypes == 0) ||
+ ((this.mTransportTypes & nc.mTransportTypes) != 0));
+ }
+ private boolean equalsTransportTypes(NetworkCapabilities nc) {
+ return (nc.mTransportTypes == this.mTransportTypes);
+ }
+
+ /**
+ * Passive link bandwidth. This is a rough guide of the expected peak bandwidth
+ * for the first hop on the given transport. It is not measured, but may take into account
+ * link parameters (Radio technology, allocated channels, etc).
+ */
+ private int mLinkUpBandwidthKbps;
+ private int mLinkDownBandwidthKbps;
+
+ /**
+ * Sets the upstream bandwidth for this network in Kbps. This always only refers to
+ * the estimated first hop transport bandwidth.
+ * <p>
+ * Note that when used to request a network, this specifies the minimum acceptable.
+ * When received as the state of an existing network this specifies the typical
+ * first hop bandwidth expected. This is never measured, but rather is inferred
+ * from technology type and other link parameters. It could be used to differentiate
+ * between very slow 1xRTT cellular links and other faster networks or even between
+ * 802.11b vs 802.11AC wifi technologies. It should not be used to differentiate between
+ * fast backhauls and slow backhauls.
+ *
+ * @param upKbps the estimated first hop upstream (device to network) bandwidth.
+ * @hide
+ */
+ public void setLinkUpstreamBandwidthKbps(int upKbps) {
+ mLinkUpBandwidthKbps = upKbps;
+ }
+
+ /**
+ * Retrieves the upstream bandwidth for this network in Kbps. This always only refers to
+ * the estimated first hop transport bandwidth.
+ *
+ * @return The estimated first hop upstream (device to network) bandwidth.
+ */
+ public int getLinkUpstreamBandwidthKbps() {
+ return mLinkUpBandwidthKbps;
+ }
+
+ /**
+ * Sets the downstream bandwidth for this network in Kbps. This always only refers to
+ * the estimated first hop transport bandwidth.
+ * <p>
+ * Note that when used to request a network, this specifies the minimum acceptable.
+ * When received as the state of an existing network this specifies the typical
+ * first hop bandwidth expected. This is never measured, but rather is inferred
+ * from technology type and other link parameters. It could be used to differentiate
+ * between very slow 1xRTT cellular links and other faster networks or even between
+ * 802.11b vs 802.11AC wifi technologies. It should not be used to differentiate between
+ * fast backhauls and slow backhauls.
+ *
+ * @param downKbps the estimated first hop downstream (network to device) bandwidth.
+ * @hide
+ */
+ public void setLinkDownstreamBandwidthKbps(int downKbps) {
+ mLinkDownBandwidthKbps = downKbps;
+ }
+
+ /**
+ * Retrieves the downstream bandwidth for this network in Kbps. This always only refers to
+ * the estimated first hop transport bandwidth.
+ *
+ * @return The estimated first hop downstream (network to device) bandwidth.
+ */
+ public int getLinkDownstreamBandwidthKbps() {
+ return mLinkDownBandwidthKbps;
+ }
+
+ private void combineLinkBandwidths(NetworkCapabilities nc) {
+ this.mLinkUpBandwidthKbps =
+ Math.max(this.mLinkUpBandwidthKbps, nc.mLinkUpBandwidthKbps);
+ this.mLinkDownBandwidthKbps =
+ Math.max(this.mLinkDownBandwidthKbps, nc.mLinkDownBandwidthKbps);
+ }
+ private boolean satisfiedByLinkBandwidths(NetworkCapabilities nc) {
+ return !(this.mLinkUpBandwidthKbps > nc.mLinkUpBandwidthKbps ||
+ this.mLinkDownBandwidthKbps > nc.mLinkDownBandwidthKbps);
+ }
+ private boolean equalsLinkBandwidths(NetworkCapabilities nc) {
+ return (this.mLinkUpBandwidthKbps == nc.mLinkUpBandwidthKbps &&
+ this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps);
+ }
+
+ private String mNetworkSpecifier;
+ /**
+ * Sets the optional bearer specific network specifier.
+ * This has no meaning if a single transport is also not specified, so calling
+ * this without a single transport set will generate an exception, as will
+ * subsequently adding or removing transports after this is set.
+ * </p>
+ * The interpretation of this {@code String} is bearer specific and bearers that use
+ * it should document their particulars. For example, Bluetooth may use some sort of
+ * device id while WiFi could used SSID and/or BSSID. Cellular may use carrier SPN (name)
+ * or Subscription ID.
+ *
+ * @param networkSpecifier An {@code String} of opaque format used to specify the bearer
+ * specific network specifier where the bearer has a choice of
+ * networks.
+ * @hide
+ */
+ public void setNetworkSpecifier(String networkSpecifier) {
+ if (TextUtils.isEmpty(networkSpecifier) == false && Long.bitCount(mTransportTypes) != 1) {
+ throw new IllegalStateException("Must have a single transport specified to use " +
+ "setNetworkSpecifier");
+ }
+ mNetworkSpecifier = networkSpecifier;
+ }
+
+ /**
+ * Gets the optional bearer specific network specifier.
+ *
+ * @return The optional {@code String} specifying the bearer specific network specifier.
+ * See {@link #setNetworkSpecifier}.
+ * @hide
+ */
+ public String getNetworkSpecifier() {
+ return mNetworkSpecifier;
+ }
+
+ private void combineSpecifiers(NetworkCapabilities nc) {
+ String otherSpecifier = nc.getNetworkSpecifier();
+ if (TextUtils.isEmpty(otherSpecifier)) return;
+ if (TextUtils.isEmpty(mNetworkSpecifier) == false) {
+ throw new IllegalStateException("Can't combine two networkSpecifiers");
+ }
+ setNetworkSpecifier(otherSpecifier);
+ }
+ private boolean satisfiedBySpecifier(NetworkCapabilities nc) {
+ return (TextUtils.isEmpty(mNetworkSpecifier) ||
+ mNetworkSpecifier.equals(nc.mNetworkSpecifier));
+ }
+ private boolean equalsSpecifier(NetworkCapabilities nc) {
+ if (TextUtils.isEmpty(mNetworkSpecifier)) {
+ return TextUtils.isEmpty(nc.mNetworkSpecifier);
+ } else {
+ return mNetworkSpecifier.equals(nc.mNetworkSpecifier);
+ }
+ }
+
+ /**
+ * Combine a set of Capabilities to this one. Useful for coming up with the complete set
+ * {@hide}
+ */
+ public void combineCapabilities(NetworkCapabilities nc) {
+ combineNetCapabilities(nc);
+ combineTransportTypes(nc);
+ combineLinkBandwidths(nc);
+ combineSpecifiers(nc);
+ }
+
+ /**
+ * Check if our requirements are satisfied by the given Capabilities.
+ * {@hide}
+ */
+ public boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc) {
+ return (nc != null &&
+ satisfiedByNetCapabilities(nc) &&
+ satisfiedByTransportTypes(nc) &&
+ satisfiedByLinkBandwidths(nc) &&
+ satisfiedBySpecifier(nc));
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || (obj instanceof NetworkCapabilities == false)) return false;
+ NetworkCapabilities that = (NetworkCapabilities)obj;
+ return (equalsNetCapabilities(that) &&
+ equalsTransportTypes(that) &&
+ equalsLinkBandwidths(that) &&
+ equalsSpecifier(that));
+ }
+
+ @Override
+ public int hashCode() {
+ return ((int)(mNetworkCapabilities & 0xFFFFFFFF) +
+ ((int)(mNetworkCapabilities >> 32) * 3) +
+ ((int)(mTransportTypes & 0xFFFFFFFF) * 5) +
+ ((int)(mTransportTypes >> 32) * 7) +
+ (mLinkUpBandwidthKbps * 11) +
+ (mLinkDownBandwidthKbps * 13) +
+ (TextUtils.isEmpty(mNetworkSpecifier) ? 0 : mNetworkSpecifier.hashCode() * 17));
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mNetworkCapabilities);
+ dest.writeLong(mTransportTypes);
+ dest.writeInt(mLinkUpBandwidthKbps);
+ dest.writeInt(mLinkDownBandwidthKbps);
+ dest.writeString(mNetworkSpecifier);
+ }
+ public static final Creator<NetworkCapabilities> CREATOR =
+ new Creator<NetworkCapabilities>() {
+ public NetworkCapabilities createFromParcel(Parcel in) {
+ NetworkCapabilities netCap = new NetworkCapabilities();
+
+ netCap.mNetworkCapabilities = in.readLong();
+ netCap.mTransportTypes = in.readLong();
+ netCap.mLinkUpBandwidthKbps = in.readInt();
+ netCap.mLinkDownBandwidthKbps = in.readInt();
+ netCap.mNetworkSpecifier = in.readString();
+ return netCap;
+ }
+ public NetworkCapabilities[] newArray(int size) {
+ return new NetworkCapabilities[size];
+ }
+ };
+
+ public String toString() {
+ int[] types = getTransportTypes();
+ String transports = (types.length > 0 ? " Transports: " : "");
+ for (int i = 0; i < types.length;) {
+ switch (types[i]) {
+ case TRANSPORT_CELLULAR: transports += "CELLULAR"; break;
+ case TRANSPORT_WIFI: transports += "WIFI"; break;
+ case TRANSPORT_BLUETOOTH: transports += "BLUETOOTH"; break;
+ case TRANSPORT_ETHERNET: transports += "ETHERNET"; break;
+ case TRANSPORT_VPN: transports += "VPN"; break;
+ }
+ if (++i < types.length) transports += "|";
+ }
+
+ types = getCapabilities();
+ String capabilities = (types.length > 0 ? " Capabilities: " : "");
+ for (int i = 0; i < types.length; ) {
+ switch (types[i]) {
+ case NET_CAPABILITY_MMS: capabilities += "MMS"; break;
+ case NET_CAPABILITY_SUPL: capabilities += "SUPL"; break;
+ case NET_CAPABILITY_DUN: capabilities += "DUN"; break;
+ case NET_CAPABILITY_FOTA: capabilities += "FOTA"; break;
+ case NET_CAPABILITY_IMS: capabilities += "IMS"; break;
+ case NET_CAPABILITY_CBS: capabilities += "CBS"; break;
+ case NET_CAPABILITY_WIFI_P2P: capabilities += "WIFI_P2P"; break;
+ case NET_CAPABILITY_IA: capabilities += "IA"; break;
+ case NET_CAPABILITY_RCS: capabilities += "RCS"; break;
+ case NET_CAPABILITY_XCAP: capabilities += "XCAP"; break;
+ case NET_CAPABILITY_EIMS: capabilities += "EIMS"; break;
+ case NET_CAPABILITY_NOT_METERED: capabilities += "NOT_METERED"; break;
+ case NET_CAPABILITY_INTERNET: capabilities += "INTERNET"; break;
+ case NET_CAPABILITY_NOT_RESTRICTED: capabilities += "NOT_RESTRICTED"; break;
+ case NET_CAPABILITY_TRUSTED: capabilities += "TRUSTED"; break;
+ case NET_CAPABILITY_NOT_VPN: capabilities += "NOT_VPN"; break;
+ }
+ if (++i < types.length) capabilities += "&";
+ }
+
+ String upBand = ((mLinkUpBandwidthKbps > 0) ? " LinkUpBandwidth>=" +
+ mLinkUpBandwidthKbps + "Kbps" : "");
+ String dnBand = ((mLinkDownBandwidthKbps > 0) ? " LinkDnBandwidth>=" +
+ mLinkDownBandwidthKbps + "Kbps" : "");
+
+ String specifier = (mNetworkSpecifier == null ?
+ "" : " Specifier: <" + mNetworkSpecifier + ">");
+
+ return "[" + transports + capabilities + upBand + dnBand + specifier + "]";
+ }
+}
diff --git a/core/java/android/net/NetworkConfig.java b/core/java/android/net/NetworkConfig.java
index 5d95f41..32a2cda 100644
--- a/core/java/android/net/NetworkConfig.java
+++ b/core/java/android/net/NetworkConfig.java
@@ -16,7 +16,6 @@
package android.net;
-import android.util.Log;
import java.util.Locale;
/**
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 4d2a70d..d279412 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -156,18 +156,20 @@
/** {@hide} */
public NetworkInfo(NetworkInfo source) {
if (source != null) {
- mNetworkType = source.mNetworkType;
- mSubtype = source.mSubtype;
- mTypeName = source.mTypeName;
- mSubtypeName = source.mSubtypeName;
- mState = source.mState;
- mDetailedState = source.mDetailedState;
- mReason = source.mReason;
- mExtraInfo = source.mExtraInfo;
- mIsFailover = source.mIsFailover;
- mIsRoaming = source.mIsRoaming;
- mIsAvailable = source.mIsAvailable;
- mIsConnectedToProvisioningNetwork = source.mIsConnectedToProvisioningNetwork;
+ synchronized (source) {
+ mNetworkType = source.mNetworkType;
+ mSubtype = source.mSubtype;
+ mTypeName = source.mTypeName;
+ mSubtypeName = source.mSubtypeName;
+ mState = source.mState;
+ mDetailedState = source.mDetailedState;
+ mReason = source.mReason;
+ mExtraInfo = source.mExtraInfo;
+ mIsFailover = source.mIsFailover;
+ mIsRoaming = source.mIsRoaming;
+ mIsAvailable = source.mIsAvailable;
+ mIsConnectedToProvisioningNetwork = source.mIsConnectedToProvisioningNetwork;
+ }
}
}
@@ -186,6 +188,15 @@
}
/**
+ * @hide
+ */
+ public void setType(int type) {
+ synchronized (this) {
+ mNetworkType = type;
+ }
+ }
+
+ /**
* Return a network-type-specific integer describing the subtype
* of the network.
* @return the network subtype
@@ -196,7 +207,10 @@
}
}
- void setSubtype(int subtype, String subtypeName) {
+ /**
+ * @hide
+ */
+ public void setSubtype(int subtype, String subtypeName) {
synchronized (this) {
mSubtype = subtype;
mSubtypeName = subtypeName;
@@ -418,7 +432,7 @@
@Override
public String toString() {
synchronized (this) {
- StringBuilder builder = new StringBuilder("NetworkInfo: ");
+ StringBuilder builder = new StringBuilder("[");
builder.append("type: ").append(getTypeName()).append("[").append(getSubtypeName()).
append("], state: ").append(mState).append("/").append(mDetailedState).
append(", reason: ").append(mReason == null ? "(unspecified)" : mReason).
@@ -427,7 +441,8 @@
append(", failover: ").append(mIsFailover).
append(", isAvailable: ").append(mIsAvailable).
append(", isConnectedToProvisioningNetwork: ").
- append(mIsConnectedToProvisioningNetwork);
+ append(mIsConnectedToProvisioningNetwork).
+ append("]");
return builder.toString();
}
}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
new file mode 100644
index 0000000..83bdfaa
--- /dev/null
+++ b/core/java/android/net/NetworkRequest.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2014 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.Parcel;
+import android.os.Parcelable;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Defines a request for a network, made through {@link NetworkRequest.Builder} and used
+ * to request a network via {@link ConnectivityManager#requestNetwork} or listen for changes
+ * via {@link ConnectivityManager#registerNetworkCallback}.
+ */
+public class NetworkRequest implements Parcelable {
+ /**
+ * The {@link NetworkCapabilities} that define this request.
+ * @hide
+ */
+ public final NetworkCapabilities networkCapabilities;
+
+ /**
+ * Identifies the request. NetworkRequests should only be constructed by
+ * the Framework and given out to applications as tokens to be used to identify
+ * the request.
+ * @hide
+ */
+ public final int requestId;
+
+ /**
+ * Set for legacy requests and the default. Set to TYPE_NONE for none.
+ * Causes CONNECTIVITY_ACTION broadcasts to be sent.
+ * @hide
+ */
+ public final int legacyType;
+
+ /**
+ * @hide
+ */
+ public NetworkRequest(NetworkCapabilities nc, int legacyType, int rId) {
+ requestId = rId;
+ networkCapabilities = nc;
+ this.legacyType = legacyType;
+ }
+
+ /**
+ * @hide
+ */
+ public NetworkRequest(NetworkRequest that) {
+ networkCapabilities = new NetworkCapabilities(that.networkCapabilities);
+ requestId = that.requestId;
+ this.legacyType = that.legacyType;
+ }
+
+ /**
+ * Builder used to create {@link NetworkRequest} objects. Specify the Network features
+ * needed in terms of {@link NetworkCapabilities} features
+ */
+ public static class Builder {
+ private final NetworkCapabilities mNetworkCapabilities = new NetworkCapabilities();
+
+ /**
+ * Default constructor for Builder.
+ */
+ public Builder() {}
+
+ /**
+ * Build {@link NetworkRequest} give the current set of capabilities.
+ */
+ public NetworkRequest build() {
+ return new NetworkRequest(mNetworkCapabilities, ConnectivityManager.TYPE_NONE,
+ ConnectivityManager.REQUEST_ID_UNSET);
+ }
+
+ /**
+ * Add the given capability requirement to this builder. These represent
+ * the requested network's required capabilities. Note that when searching
+ * for a network to satisfy a request, all capabilities requested must be
+ * satisfied. See {@link NetworkCapabilities} for {@code NET_CAPABILITIY_*}
+ * definitions.
+ *
+ * @param capability The {@code NetworkCapabilities.NET_CAPABILITY_*} to add.
+ * @return The builder to facilitate chaining
+ * {@code builder.addCapability(...).addCapability();}.
+ */
+ public Builder addCapability(int capability) {
+ mNetworkCapabilities.addCapability(capability);
+ return this;
+ }
+
+ /**
+ * Removes (if found) the given capability from this builder instance.
+ *
+ * @param capability The {@code NetworkCapabilities.NET_CAPABILITY_*} to remove.
+ * @return The builder to facilitate chaining.
+ */
+ public Builder removeCapability(int capability) {
+ mNetworkCapabilities.removeCapability(capability);
+ return this;
+ }
+
+ /**
+ * Adds the given transport requirement to this builder. These represent
+ * the set of allowed transports for the request. Only networks using one
+ * of these transports will satisfy the request. If no particular transports
+ * are required, none should be specified here. See {@link NetworkCapabilities}
+ * for {@code TRANSPORT_*} definitions.
+ *
+ * @param transportType The {@code NetworkCapabilities.TRANSPORT_*} to add.
+ * @return The builder to facilitate chaining.
+ */
+ public Builder addTransportType(int transportType) {
+ mNetworkCapabilities.addTransportType(transportType);
+ return this;
+ }
+
+ /**
+ * Removes (if found) the given transport from this builder instance.
+ *
+ * @param transportType The {@code NetworkCapabilities.TRANSPORT_*} to remove.
+ * @return The builder to facilitate chaining.
+ */
+ public Builder removeTransportType(int transportType) {
+ mNetworkCapabilities.removeTransportType(transportType);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ public Builder setLinkUpstreamBandwidthKbps(int upKbps) {
+ mNetworkCapabilities.setLinkUpstreamBandwidthKbps(upKbps);
+ return this;
+ }
+ /**
+ * @hide
+ */
+ public Builder setLinkDownstreamBandwidthKbps(int downKbps) {
+ mNetworkCapabilities.setLinkDownstreamBandwidthKbps(downKbps);
+ return this;
+ }
+
+ /**
+ * Sets the optional bearer specific network specifier.
+ * This has no meaning if a single transport is also not specified, so calling
+ * this without a single transport set will generate an exception, as will
+ * subsequently adding or removing transports after this is set.
+ * </p>
+ * The interpretation of this {@code String} is bearer specific and bearers that use
+ * it should document their particulars. For example, Bluetooth may use some sort of
+ * device id while WiFi could used ssid and/or bssid. Cellular may use carrier spn.
+ *
+ * @param networkSpecifier An {@code String} of opaque format used to specify the bearer
+ * specific network specifier where the bearer has a choice of
+ * networks.
+ */
+ public Builder setNetworkSpecifier(String networkSpecifier) {
+ mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
+ return this;
+ }
+ }
+
+ // implement the Parcelable interface
+ public int describeContents() {
+ return 0;
+ }
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(networkCapabilities, flags);
+ dest.writeInt(legacyType);
+ dest.writeInt(requestId);
+ }
+ public static final Creator<NetworkRequest> CREATOR =
+ new Creator<NetworkRequest>() {
+ public NetworkRequest createFromParcel(Parcel in) {
+ NetworkCapabilities nc = (NetworkCapabilities)in.readParcelable(null);
+ int legacyType = in.readInt();
+ int requestId = in.readInt();
+ NetworkRequest result = new NetworkRequest(nc, legacyType, requestId);
+ return result;
+ }
+ public NetworkRequest[] newArray(int size) {
+ return new NetworkRequest[size];
+ }
+ };
+
+ public String toString() {
+ return "NetworkRequest [ id=" + requestId + ", legacyType=" + legacyType +
+ ", " + networkCapabilities.toString() + " ]";
+ }
+
+ public boolean equals(Object obj) {
+ if (obj instanceof NetworkRequest == false) return false;
+ NetworkRequest that = (NetworkRequest)obj;
+ return (that.legacyType == this.legacyType &&
+ that.requestId == this.requestId &&
+ ((that.networkCapabilities == null && this.networkCapabilities == null) ||
+ (that.networkCapabilities != null &&
+ that.networkCapabilities.equals(this.networkCapabilities))));
+ }
+
+ public int hashCode() {
+ return requestId + (legacyType * 1013) +
+ (networkCapabilities.hashCode() * 1051);
+ }
+}
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index fbe1f82..2e0e9e4 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -28,21 +28,21 @@
public final NetworkInfo networkInfo;
public final LinkProperties linkProperties;
- public final LinkCapabilities linkCapabilities;
+ public final NetworkCapabilities networkCapabilities;
/** Currently only used by testing. */
public final String subscriberId;
public final String networkId;
public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
- LinkCapabilities linkCapabilities) {
- this(networkInfo, linkProperties, linkCapabilities, null, null);
+ NetworkCapabilities networkCapabilities) {
+ this(networkInfo, linkProperties, networkCapabilities, null, null);
}
public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
- LinkCapabilities linkCapabilities, String subscriberId, String networkId) {
+ NetworkCapabilities networkCapabilities, String subscriberId, String networkId) {
this.networkInfo = networkInfo;
this.linkProperties = linkProperties;
- this.linkCapabilities = linkCapabilities;
+ this.networkCapabilities = networkCapabilities;
this.subscriberId = subscriberId;
this.networkId = networkId;
}
@@ -50,7 +50,7 @@
public NetworkState(Parcel in) {
networkInfo = in.readParcelable(null);
linkProperties = in.readParcelable(null);
- linkCapabilities = in.readParcelable(null);
+ networkCapabilities = in.readParcelable(null);
subscriberId = in.readString();
networkId = in.readString();
}
@@ -64,7 +64,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeParcelable(networkInfo, flags);
out.writeParcelable(linkProperties, flags);
- out.writeParcelable(linkCapabilities, flags);
+ out.writeParcelable(networkCapabilities, flags);
out.writeString(subscriberId);
out.writeString(networkId);
}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index b24d396..af860b0 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -24,6 +24,8 @@
import java.util.Locale;
import android.util.Log;
+import android.util.Pair;
+
/**
* Native methods for managing network interfaces.
@@ -109,6 +111,44 @@
public native static void markSocket(int socketfd, int mark);
/**
+ * Binds the current process to the network designated by {@code netId}. All sockets created
+ * in the future (and not explicitly bound via a bound {@link SocketFactory} (see
+ * {@link Network#getSocketFactory}) will be bound to this network. Note that if this
+ * {@code Network} ever disconnects all sockets created in this way will cease to work. This
+ * is by design so an application doesn't accidentally use sockets it thinks are still bound to
+ * a particular {@code Network}. Passing NETID_UNSET clears the binding.
+ */
+ public native static boolean bindProcessToNetwork(int netId);
+
+ /**
+ * Return the netId last passed to {@link #bindProcessToNetwork}, or NETID_UNSET if
+ * {@link #unbindProcessToNetwork} has been called since {@link #bindProcessToNetwork}.
+ */
+ public native static int getNetworkBoundToProcess();
+
+ /**
+ * Binds host resolutions performed by this process to the network designated by {@code netId}.
+ * {@link #bindProcessToNetwork} takes precedence over this setting. Passing NETID_UNSET clears
+ * the binding.
+ *
+ * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+ */
+ public native static boolean bindProcessToNetworkForHostResolution(int netId);
+
+ /**
+ * Explicitly binds {@code socketfd} to the network designated by {@code netId}. This
+ * overrides any binding via {@link #bindProcessToNetwork}.
+ */
+ public native static boolean bindSocketToNetwork(int socketfd, int netId);
+
+ /**
+ * Protect {@code socketfd} from VPN connections. After protecting, data sent through
+ * this socket will go directly to the underlying network, so its traffic will not be
+ * forwarded through the VPN.
+ */
+ public native static boolean protectFromVpn(int socketfd);
+
+ /**
* Convert a IPv4 address from an integer to an InetAddress.
* @param hostAddress an int corresponding to the IPv4 address in network byte order
*/
@@ -174,24 +214,17 @@
}
/**
- * Get InetAddress masked with prefixLength. Will never return null.
- * @param IP address which will be masked with specified prefixLength
- * @param prefixLength the prefixLength used to mask the IP
+ * Masks a raw IP address byte array with the specified prefix length.
*/
- public static InetAddress getNetworkPart(InetAddress address, int prefixLength) {
- if (address == null) {
- throw new RuntimeException("getNetworkPart doesn't accept null address");
- }
-
- byte[] array = address.getAddress();
-
+ public static void maskRawAddress(byte[] array, int prefixLength) {
if (prefixLength < 0 || prefixLength > array.length * 8) {
- throw new RuntimeException("getNetworkPart - bad prefixLength");
+ throw new RuntimeException("IP address with " + array.length +
+ " bytes has invalid prefix length " + prefixLength);
}
int offset = prefixLength / 8;
- int reminder = prefixLength % 8;
- byte mask = (byte)(0xFF << (8 - reminder));
+ int remainder = prefixLength % 8;
+ byte mask = (byte)(0xFF << (8 - remainder));
if (offset < array.length) array[offset] = (byte)(array[offset] & mask);
@@ -200,6 +233,16 @@
for (; offset < array.length; offset++) {
array[offset] = 0;
}
+ }
+
+ /**
+ * Get InetAddress masked with prefixLength. Will never return null.
+ * @param address the IP address to mask with
+ * @param prefixLength the prefixLength used to mask the IP
+ */
+ public static InetAddress getNetworkPart(InetAddress address, int prefixLength) {
+ byte[] array = address.getAddress();
+ maskRawAddress(array, prefixLength);
InetAddress netPart = null;
try {
@@ -211,6 +254,30 @@
}
/**
+ * Utility method to parse strings such as "192.0.2.5/24" or "2001:db8::cafe:d00d/64".
+ * @hide
+ */
+ public static Pair<InetAddress, Integer> parseIpAndMask(String ipAndMaskString) {
+ InetAddress address = null;
+ int prefixLength = -1;
+ try {
+ String[] pieces = ipAndMaskString.split("/", 2);
+ prefixLength = Integer.parseInt(pieces[1]);
+ address = InetAddress.parseNumericAddress(pieces[0]);
+ } catch (NullPointerException e) { // Null string.
+ } catch (ArrayIndexOutOfBoundsException e) { // No prefix length.
+ } catch (NumberFormatException e) { // Non-numeric prefix.
+ } catch (IllegalArgumentException e) { // Invalid IP address.
+ }
+
+ if (address == null || prefixLength == -1) {
+ throw new IllegalArgumentException("Invalid IP address and mask " + ipAndMaskString);
+ }
+
+ return new Pair<InetAddress, Integer>(address, prefixLength);
+ }
+
+ /**
* Check if IP address type is consistent between two InetAddress.
* @return true if both are the same type. False otherwise.
*/
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
new file mode 100644
index 0000000..7ea6bae
--- /dev/null
+++ b/core/java/android/net/ProxyInfo.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2010 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.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import org.apache.http.client.HttpClient;
+
+import java.net.InetSocketAddress;
+import java.net.URLConnection;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Describes a proxy configuration.
+ *
+ * Proxy configurations are already integrated within the Apache HTTP stack.
+ * So {@link URLConnection} and {@link HttpClient} will use them automatically.
+ *
+ * Other HTTP stacks will need to obtain the proxy info from
+ * {@link Proxy#PROXY_CHANGE_ACTION} broadcast as the extra {@link Proxy#EXTRA_PROXY_INFO}.
+ */
+public class ProxyInfo implements Parcelable {
+
+ private String mHost;
+ private int mPort;
+ private String mExclusionList;
+ private String[] mParsedExclusionList;
+
+ private Uri mPacFileUrl;
+ /**
+ *@hide
+ */
+ public static final String LOCAL_EXCL_LIST = "";
+ /**
+ *@hide
+ */
+ public static final int LOCAL_PORT = -1;
+ /**
+ *@hide
+ */
+ public static final String LOCAL_HOST = "localhost";
+
+ /**
+ * Constructs a {@link ProxyInfo} object that points at a Direct proxy
+ * on the specified host and port.
+ */
+ public static ProxyInfo buildDirectProxy(String host, int port) {
+ return new ProxyInfo(host, port, null);
+ }
+
+ /**
+ * Constructs a {@link ProxyInfo} object that points at a Direct proxy
+ * on the specified host and port.
+ *
+ * The proxy will not be used to access any host in exclusion list, exclList.
+ *
+ * @param exclList Hosts to exclude using the proxy on connections for. These
+ * hosts can use wildcards such as *.example.com.
+ */
+ public static ProxyInfo buildDirectProxy(String host, int port, List<String> exclList) {
+ String[] array = exclList.toArray(new String[exclList.size()]);
+ return new ProxyInfo(host, port, TextUtils.join(",", array), array);
+ }
+
+ /**
+ * Construct a {@link ProxyInfo} that will download and run the PAC script
+ * at the specified URL.
+ */
+ public static ProxyInfo buildPacProxy(Uri pacUri) {
+ return new ProxyInfo(pacUri);
+ }
+
+ /**
+ * Create a ProxyProperties that points at a HTTP Proxy.
+ * @hide
+ */
+ public ProxyInfo(String host, int port, String exclList) {
+ mHost = host;
+ mPort = port;
+ setExclusionList(exclList);
+ mPacFileUrl = Uri.EMPTY;
+ }
+
+ /**
+ * Create a ProxyProperties that points at a PAC URL.
+ * @hide
+ */
+ public ProxyInfo(Uri pacFileUrl) {
+ mHost = LOCAL_HOST;
+ mPort = LOCAL_PORT;
+ setExclusionList(LOCAL_EXCL_LIST);
+ if (pacFileUrl == null) {
+ throw new NullPointerException();
+ }
+ mPacFileUrl = pacFileUrl;
+ }
+
+ /**
+ * Create a ProxyProperties that points at a PAC URL.
+ * @hide
+ */
+ public ProxyInfo(String pacFileUrl) {
+ mHost = LOCAL_HOST;
+ mPort = LOCAL_PORT;
+ setExclusionList(LOCAL_EXCL_LIST);
+ mPacFileUrl = Uri.parse(pacFileUrl);
+ }
+
+ /**
+ * Only used in PacManager after Local Proxy is bound.
+ * @hide
+ */
+ public ProxyInfo(Uri pacFileUrl, int localProxyPort) {
+ mHost = LOCAL_HOST;
+ mPort = localProxyPort;
+ setExclusionList(LOCAL_EXCL_LIST);
+ if (pacFileUrl == null) {
+ throw new NullPointerException();
+ }
+ mPacFileUrl = pacFileUrl;
+ }
+
+ private ProxyInfo(String host, int port, String exclList, String[] parsedExclList) {
+ mHost = host;
+ mPort = port;
+ mExclusionList = exclList;
+ mParsedExclusionList = parsedExclList;
+ mPacFileUrl = Uri.EMPTY;
+ }
+
+ // copy constructor instead of clone
+ /**
+ * @hide
+ */
+ public ProxyInfo(ProxyInfo source) {
+ if (source != null) {
+ mHost = source.getHost();
+ mPort = source.getPort();
+ mPacFileUrl = source.mPacFileUrl;
+ mExclusionList = source.getExclusionListAsString();
+ mParsedExclusionList = source.mParsedExclusionList;
+ } else {
+ mPacFileUrl = Uri.EMPTY;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public InetSocketAddress getSocketAddress() {
+ InetSocketAddress inetSocketAddress = null;
+ try {
+ inetSocketAddress = new InetSocketAddress(mHost, mPort);
+ } catch (IllegalArgumentException e) { }
+ return inetSocketAddress;
+ }
+
+ /**
+ * Returns the URL of the current PAC script or null if there is
+ * no PAC script.
+ */
+ public Uri getPacFileUrl() {
+ return mPacFileUrl;
+ }
+
+ /**
+ * When configured to use a Direct Proxy this returns the host
+ * of the proxy.
+ */
+ public String getHost() {
+ return mHost;
+ }
+
+ /**
+ * When configured to use a Direct Proxy this returns the port
+ * of the proxy
+ */
+ public int getPort() {
+ return mPort;
+ }
+
+ /**
+ * When configured to use a Direct Proxy this returns the list
+ * of hosts for which the proxy is ignored.
+ */
+ public String[] getExclusionList() {
+ return mParsedExclusionList;
+ }
+
+ /**
+ * comma separated
+ * @hide
+ */
+ public String getExclusionListAsString() {
+ return mExclusionList;
+ }
+
+ // comma separated
+ private void setExclusionList(String exclusionList) {
+ mExclusionList = exclusionList;
+ if (mExclusionList == null) {
+ mParsedExclusionList = new String[0];
+ } else {
+ mParsedExclusionList = exclusionList.toLowerCase(Locale.ROOT).split(",");
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isValid() {
+ if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
+ return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
+ mPort == 0 ? "" : Integer.toString(mPort),
+ mExclusionList == null ? "" : mExclusionList);
+ }
+
+ /**
+ * @hide
+ */
+ public java.net.Proxy makeProxy() {
+ java.net.Proxy proxy = java.net.Proxy.NO_PROXY;
+ if (mHost != null) {
+ try {
+ InetSocketAddress inetSocketAddress = new InetSocketAddress(mHost, mPort);
+ proxy = new java.net.Proxy(java.net.Proxy.Type.HTTP, inetSocketAddress);
+ } catch (IllegalArgumentException e) {
+ }
+ }
+ return proxy;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (!Uri.EMPTY.equals(mPacFileUrl)) {
+ sb.append("PAC Script: ");
+ sb.append(mPacFileUrl);
+ } else if (mHost != null) {
+ sb.append("[");
+ sb.append(mHost);
+ sb.append("] ");
+ sb.append(Integer.toString(mPort));
+ if (mExclusionList != null) {
+ sb.append(" xl=").append(mExclusionList);
+ }
+ } else {
+ sb.append("[ProxyProperties.mHost == null]");
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ProxyInfo)) return false;
+ ProxyInfo p = (ProxyInfo)o;
+ // If PAC URL is present in either then they must be equal.
+ // Other parameters will only be for fall back.
+ if (!Uri.EMPTY.equals(mPacFileUrl)) {
+ return mPacFileUrl.equals(p.getPacFileUrl()) && mPort == p.mPort;
+ }
+ if (!Uri.EMPTY.equals(p.mPacFileUrl)) {
+ return false;
+ }
+ if (mExclusionList != null && !mExclusionList.equals(p.getExclusionListAsString())) {
+ return false;
+ }
+ if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) {
+ return false;
+ }
+ if (mHost != null && p.mHost == null) return false;
+ if (mHost == null && p.mHost != null) return false;
+ if (mPort != p.mPort) return false;
+ return true;
+ }
+
+ /**
+ * Implement the Parcelable interface
+ * @hide
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ /*
+ * generate hashcode based on significant fields
+ */
+ public int hashCode() {
+ return ((null == mHost) ? 0 : mHost.hashCode())
+ + ((null == mExclusionList) ? 0 : mExclusionList.hashCode())
+ + mPort;
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ * @hide
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ if (!Uri.EMPTY.equals(mPacFileUrl)) {
+ dest.writeByte((byte)1);
+ mPacFileUrl.writeToParcel(dest, 0);
+ dest.writeInt(mPort);
+ return;
+ } else {
+ dest.writeByte((byte)0);
+ }
+ if (mHost != null) {
+ dest.writeByte((byte)1);
+ dest.writeString(mHost);
+ dest.writeInt(mPort);
+ } else {
+ dest.writeByte((byte)0);
+ }
+ dest.writeString(mExclusionList);
+ dest.writeStringArray(mParsedExclusionList);
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ * @hide
+ */
+ public static final Creator<ProxyInfo> CREATOR =
+ new Creator<ProxyInfo>() {
+ public ProxyInfo createFromParcel(Parcel in) {
+ String host = null;
+ int port = 0;
+ if (in.readByte() != 0) {
+ Uri url = Uri.CREATOR.createFromParcel(in);
+ int localPort = in.readInt();
+ return new ProxyInfo(url, localPort);
+ }
+ if (in.readByte() != 0) {
+ host = in.readString();
+ port = in.readInt();
+ }
+ String exclList = in.readString();
+ String[] parsedExclList = in.readStringArray();
+ ProxyInfo proxyProperties =
+ new ProxyInfo(host, port, exclList, parsedExclList);
+ return proxyProperties;
+ }
+
+ public ProxyInfo[] newArray(int size) {
+ return new ProxyInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyProperties.java
deleted file mode 100644
index 010e527..0000000
--- a/core/java/android/net/ProxyProperties.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (C) 2010 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.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
-import java.util.Locale;
-
-/**
- * A container class for the http proxy info
- * @hide
- */
-public class ProxyProperties implements Parcelable {
-
- private String mHost;
- private int mPort;
- private String mExclusionList;
- private String[] mParsedExclusionList;
-
- private String mPacFileUrl;
- public static final String LOCAL_EXCL_LIST = "";
- public static final int LOCAL_PORT = -1;
- public static final String LOCAL_HOST = "localhost";
-
- public ProxyProperties(String host, int port, String exclList) {
- mHost = host;
- mPort = port;
- setExclusionList(exclList);
- }
-
- public ProxyProperties(String pacFileUrl) {
- mHost = LOCAL_HOST;
- mPort = LOCAL_PORT;
- setExclusionList(LOCAL_EXCL_LIST);
- mPacFileUrl = pacFileUrl;
- }
-
- // Only used in PacManager after Local Proxy is bound.
- public ProxyProperties(String pacFileUrl, int localProxyPort) {
- mHost = LOCAL_HOST;
- mPort = localProxyPort;
- setExclusionList(LOCAL_EXCL_LIST);
- mPacFileUrl = pacFileUrl;
- }
-
- private ProxyProperties(String host, int port, String exclList, String[] parsedExclList) {
- mHost = host;
- mPort = port;
- mExclusionList = exclList;
- mParsedExclusionList = parsedExclList;
- mPacFileUrl = null;
- }
-
- // copy constructor instead of clone
- public ProxyProperties(ProxyProperties source) {
- if (source != null) {
- mHost = source.getHost();
- mPort = source.getPort();
- mPacFileUrl = source.getPacFileUrl();
- mExclusionList = source.getExclusionList();
- mParsedExclusionList = source.mParsedExclusionList;
- }
- }
-
- public InetSocketAddress getSocketAddress() {
- InetSocketAddress inetSocketAddress = null;
- try {
- inetSocketAddress = new InetSocketAddress(mHost, mPort);
- } catch (IllegalArgumentException e) { }
- return inetSocketAddress;
- }
-
- public String getPacFileUrl() {
- return mPacFileUrl;
- }
-
- public String getHost() {
- return mHost;
- }
-
- public int getPort() {
- return mPort;
- }
-
- // comma separated
- public String getExclusionList() {
- return mExclusionList;
- }
-
- // comma separated
- private void setExclusionList(String exclusionList) {
- mExclusionList = exclusionList;
- if (mExclusionList == null) {
- mParsedExclusionList = new String[0];
- } else {
- String splitExclusionList[] = exclusionList.toLowerCase(Locale.ROOT).split(",");
- mParsedExclusionList = new String[splitExclusionList.length * 2];
- for (int i = 0; i < splitExclusionList.length; i++) {
- String s = splitExclusionList[i].trim();
- if (s.startsWith(".")) s = s.substring(1);
- mParsedExclusionList[i*2] = s;
- mParsedExclusionList[(i*2)+1] = "." + s;
- }
- }
- }
-
- public boolean isExcluded(String url) {
- if (TextUtils.isEmpty(url) || mParsedExclusionList == null ||
- mParsedExclusionList.length == 0) return false;
-
- Uri u = Uri.parse(url);
- String urlDomain = u.getHost();
- if (urlDomain == null) return false;
- for (int i = 0; i< mParsedExclusionList.length; i+=2) {
- if (urlDomain.equals(mParsedExclusionList[i]) ||
- urlDomain.endsWith(mParsedExclusionList[i+1])) {
- return true;
- }
- }
- return false;
- }
-
- public boolean isValid() {
- if (!TextUtils.isEmpty(mPacFileUrl)) return true;
- try {
- Proxy.validate(mHost == null ? "" : mHost, mPort == 0 ? "" : Integer.toString(mPort),
- mExclusionList == null ? "" : mExclusionList);
- } catch (IllegalArgumentException e) {
- return false;
- }
- return true;
- }
-
- public java.net.Proxy makeProxy() {
- java.net.Proxy proxy = java.net.Proxy.NO_PROXY;
- if (mHost != null) {
- try {
- InetSocketAddress inetSocketAddress = new InetSocketAddress(mHost, mPort);
- proxy = new java.net.Proxy(java.net.Proxy.Type.HTTP, inetSocketAddress);
- } catch (IllegalArgumentException e) {
- }
- }
- return proxy;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- if (mPacFileUrl != null) {
- sb.append("PAC Script: ");
- sb.append(mPacFileUrl);
- } else if (mHost != null) {
- sb.append("[");
- sb.append(mHost);
- sb.append("] ");
- sb.append(Integer.toString(mPort));
- if (mExclusionList != null) {
- sb.append(" xl=").append(mExclusionList);
- }
- } else {
- sb.append("[ProxyProperties.mHost == null]");
- }
- return sb.toString();
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof ProxyProperties)) return false;
- ProxyProperties p = (ProxyProperties)o;
- // If PAC URL is present in either then they must be equal.
- // Other parameters will only be for fall back.
- if (!TextUtils.isEmpty(mPacFileUrl)) {
- return mPacFileUrl.equals(p.getPacFileUrl()) && mPort == p.mPort;
- }
- if (!TextUtils.isEmpty(p.getPacFileUrl())) {
- return false;
- }
- if (mExclusionList != null && !mExclusionList.equals(p.getExclusionList())) return false;
- if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) {
- return false;
- }
- if (mHost != null && p.mHost == null) return false;
- if (mHost == null && p.mHost != null) return false;
- if (mPort != p.mPort) return false;
- return true;
- }
-
- /**
- * Implement the Parcelable interface
- * @hide
- */
- public int describeContents() {
- return 0;
- }
-
- @Override
- /*
- * generate hashcode based on significant fields
- */
- public int hashCode() {
- return ((null == mHost) ? 0 : mHost.hashCode())
- + ((null == mExclusionList) ? 0 : mExclusionList.hashCode())
- + mPort;
- }
-
- /**
- * Implement the Parcelable interface.
- * @hide
- */
- public void writeToParcel(Parcel dest, int flags) {
- if (mPacFileUrl != null) {
- dest.writeByte((byte)1);
- dest.writeString(mPacFileUrl);
- dest.writeInt(mPort);
- return;
- } else {
- dest.writeByte((byte)0);
- }
- if (mHost != null) {
- dest.writeByte((byte)1);
- dest.writeString(mHost);
- dest.writeInt(mPort);
- } else {
- dest.writeByte((byte)0);
- }
- dest.writeString(mExclusionList);
- dest.writeStringArray(mParsedExclusionList);
- }
-
- /**
- * Implement the Parcelable interface.
- * @hide
- */
- public static final Creator<ProxyProperties> CREATOR =
- new Creator<ProxyProperties>() {
- public ProxyProperties createFromParcel(Parcel in) {
- String host = null;
- int port = 0;
- if (in.readByte() != 0) {
- String url = in.readString();
- int localPort = in.readInt();
- return new ProxyProperties(url, localPort);
- }
- if (in.readByte() != 0) {
- host = in.readString();
- port = in.readInt();
- }
- String exclList = in.readString();
- String[] parsedExclList = in.readStringArray();
- ProxyProperties proxyProperties =
- new ProxyProperties(host, port, exclList, parsedExclList);
- return proxyProperties;
- }
-
- public ProxyProperties[] newArray(int size) {
- return new ProxyProperties[size];
- }
- };
-}
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 1d051dd..129248c 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -25,26 +25,31 @@
import java.net.Inet6Address;
import java.util.Collection;
+import java.util.Objects;
/**
- * A simple container for route information.
+ * Represents a network route.
+ * <p>
+ * This is used both to describe static network configuration and live network
+ * configuration information.
*
- * In order to be used, a route must have a destination prefix and:
- *
- * - A gateway address (next-hop, for gatewayed routes), or
- * - An interface (for directly-connected routes), or
- * - Both a gateway and an interface.
- *
- * This class does not enforce these constraints because there is code that
- * uses RouteInfo objects to store directly-connected routes without interfaces.
- * Such objects cannot be used directly, but can be put into a LinkProperties
- * object which then specifies the interface.
- *
- * @hide
+ * A route contains three pieces of information:
+ * <ul>
+ * <li>a destination {@link IpPrefix} specifying the network destinations covered by this route.
+ * If this is {@code null} it indicates a default route of the address family (IPv4 or IPv6)
+ * implied by the gateway IP address.
+ * <li>a gateway {@link InetAddress} indicating the next hop to use. If this is {@code null} it
+ * indicates a directly-connected route.
+ * <li>an interface (which may be unspecified).
+ * </ul>
+ * Either the destination or the gateway may be {@code null}, but not both. If the
+ * destination and gateway are both specified, they must be of the same address family
+ * (IPv4 or IPv6).
*/
-public class RouteInfo implements Parcelable {
+public final class RouteInfo implements Parcelable {
/**
* The IP destination address for this route.
+ * TODO: Make this an IpPrefix.
*/
private final LinkAddress mDestination;
@@ -58,7 +63,6 @@
*/
private final String mInterface;
- private final boolean mIsDefault;
private final boolean mIsHost;
private final boolean mHasGateway;
@@ -67,15 +71,28 @@
*
* If destination is null, then gateway must be specified and the
* constructed route is either the IPv4 default route <code>0.0.0.0</code>
- * if @gateway is an instance of {@link Inet4Address}, or the IPv6 default
+ * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default
* route <code>::/0</code> if gateway is an instance of
* {@link Inet6Address}.
- *
+ * <p>
* destination and gateway may not both be null.
*
* @param destination the destination prefix
* @param gateway the IP address to route packets through
* @param iface the interface name to send packets on
+ *
+ * TODO: Convert to use IpPrefix.
+ *
+ * @hide
+ */
+ public RouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
+ this(destination == null ? null :
+ new LinkAddress(destination.getAddress(), destination.getPrefixLength()),
+ gateway, iface);
+ }
+
+ /**
+ * @hide
*/
public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
if (destination == null) {
@@ -101,29 +118,84 @@
mHasGateway = (!gateway.isAnyLocalAddress());
mDestination = new LinkAddress(NetworkUtils.getNetworkPart(destination.getAddress(),
- destination.getNetworkPrefixLength()), destination.getNetworkPrefixLength());
+ destination.getPrefixLength()), destination.getPrefixLength());
+ if ((destination.getAddress() instanceof Inet4Address &&
+ (gateway instanceof Inet4Address == false)) ||
+ (destination.getAddress() instanceof Inet6Address &&
+ (gateway instanceof Inet6Address == false))) {
+ throw new IllegalArgumentException("address family mismatch in RouteInfo constructor");
+ }
mGateway = gateway;
mInterface = iface;
- mIsDefault = isDefault();
mIsHost = isHost();
}
+ /**
+ * Constructs a {@code RouteInfo} object.
+ *
+ * If destination is null, then gateway must be specified and the
+ * constructed route is either the IPv4 default route <code>0.0.0.0</code>
+ * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default
+ * route <code>::/0</code> if gateway is an instance of {@link Inet6Address}.
+ * <p>
+ * Destination and gateway may not both be null.
+ *
+ * @param destination the destination address and prefix in an {@link IpPrefix}
+ * @param gateway the {@link InetAddress} to route packets through
+ *
+ * @hide
+ */
+ public RouteInfo(IpPrefix destination, InetAddress gateway) {
+ this(destination, gateway, null);
+ }
+
+ /**
+ * @hide
+ */
public RouteInfo(LinkAddress destination, InetAddress gateway) {
this(destination, gateway, null);
}
+ /**
+ * Constructs a default {@code RouteInfo} object.
+ *
+ * @param gateway the {@link InetAddress} to route packets through
+ *
+ * @hide
+ */
public RouteInfo(InetAddress gateway) {
- this(null, gateway, null);
+ this((LinkAddress) null, gateway, null);
}
- public RouteInfo(LinkAddress host) {
- this(host, null, null);
+ /**
+ * Constructs a {@code RouteInfo} object representing a direct connected subnet.
+ *
+ * @param destination the {@link IpPrefix} describing the address and prefix
+ * length of the subnet.
+ *
+ * @hide
+ */
+ public RouteInfo(IpPrefix destination) {
+ this(destination, null, null);
}
+ /**
+ * @hide
+ */
+ public RouteInfo(LinkAddress destination) {
+ this(destination, null, null);
+ }
+
+ /**
+ * @hide
+ */
public static RouteInfo makeHostRoute(InetAddress host, String iface) {
return makeHostRoute(host, null, iface);
}
+ /**
+ * @hide
+ */
public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway, String iface) {
if (host == null) return null;
@@ -136,67 +208,188 @@
private boolean isHost() {
return (mDestination.getAddress() instanceof Inet4Address &&
- mDestination.getNetworkPrefixLength() == 32) ||
+ mDestination.getPrefixLength() == 32) ||
(mDestination.getAddress() instanceof Inet6Address &&
- mDestination.getNetworkPrefixLength() == 128);
+ mDestination.getPrefixLength() == 128);
}
- private boolean isDefault() {
- boolean val = false;
- if (mGateway != null) {
- if (mGateway instanceof Inet4Address) {
- val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
- } else {
- val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
- }
- }
- return val;
+ /**
+ * Retrieves the destination address and prefix length in the form of an {@link IpPrefix}.
+ *
+ * @return {@link IpPrefix} specifying the destination. This is never {@code null}.
+ */
+ public IpPrefix getDestination() {
+ return new IpPrefix(mDestination.getAddress(), mDestination.getPrefixLength());
}
-
- public LinkAddress getDestination() {
+ /**
+ * TODO: Convert callers to use IpPrefix and then remove.
+ * @hide
+ */
+ public LinkAddress getDestinationLinkAddress() {
return mDestination;
}
+ /**
+ * Retrieves the gateway or next hop {@link InetAddress} for this route.
+ *
+ * @return {@link InetAddress} specifying the gateway or next hop. This may be
+ * {@code null} for a directly-connected route."
+ */
public InetAddress getGateway() {
return mGateway;
}
+ /**
+ * Retrieves the interface used for this route if specified, else {@code null}.
+ *
+ * @return The name of the interface used for this route.
+ */
public String getInterface() {
return mInterface;
}
+ /**
+ * Indicates if this route is a default route (ie, has no destination specified).
+ *
+ * @return {@code true} if the destination has a prefix length of 0.
+ */
public boolean isDefaultRoute() {
- return mIsDefault;
+ return mDestination.getPrefixLength() == 0;
}
+ /**
+ * Indicates if this route is an IPv4 default route.
+ * @hide
+ */
+ public boolean isIPv4Default() {
+ return isDefaultRoute() && mDestination.getAddress() instanceof Inet4Address;
+ }
+
+ /**
+ * Indicates if this route is an IPv6 default route.
+ * @hide
+ */
+ public boolean isIPv6Default() {
+ return isDefaultRoute() && mDestination.getAddress() instanceof Inet6Address;
+ }
+
+ /**
+ * Indicates if this route is a host route (ie, matches only a single host address).
+ *
+ * @return {@code true} if the destination has a prefix length of 32 or 128 for IPv4 or IPv6,
+ * respectively.
+ * @hide
+ */
public boolean isHostRoute() {
return mIsHost;
}
+ /**
+ * Indicates if this route has a next hop ({@code true}) or is directly-connected
+ * ({@code false}).
+ *
+ * @return {@code true} if a gateway is specified
+ * @hide
+ */
public boolean hasGateway() {
return mHasGateway;
}
+ /**
+ * Determines whether the destination and prefix of this route includes the specified
+ * address.
+ *
+ * @param destination A {@link InetAddress} to test to see if it would match this route.
+ * @return {@code true} if the destination and prefix length cover the given address.
+ */
+ public boolean matches(InetAddress destination) {
+ if (destination == null) return false;
+
+ // match the route destination and destination with prefix length
+ InetAddress dstNet = NetworkUtils.getNetworkPart(destination,
+ mDestination.getPrefixLength());
+
+ return mDestination.getAddress().equals(dstNet);
+ }
+
+ /**
+ * Find the route from a Collection of routes that best matches a given address.
+ * May return null if no routes are applicable.
+ * @param routes a Collection of RouteInfos to chose from
+ * @param dest the InetAddress your trying to get to
+ * @return the RouteInfo from the Collection that best fits the given address
+ *
+ * @hide
+ */
+ public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
+ if ((routes == null) || (dest == null)) return null;
+
+ RouteInfo bestRoute = null;
+ // pick a longest prefix match under same address type
+ for (RouteInfo route : routes) {
+ if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
+ if ((bestRoute != null) &&
+ (bestRoute.mDestination.getPrefixLength() >=
+ route.mDestination.getPrefixLength())) {
+ continue;
+ }
+ if (route.matches(dest)) bestRoute = route;
+ }
+ }
+ return bestRoute;
+ }
+
+ /**
+ * Returns a human-readable description of this object.
+ */
public String toString() {
String val = "";
if (mDestination != null) val = mDestination.toString();
- if (mGateway != null) val += " -> " + mGateway.getHostAddress();
+ val += " ->";
+ if (mGateway != null) val += " " + mGateway.getHostAddress();
+ if (mInterface != null) val += " " + mInterface;
return val;
}
+ /**
+ * Compares this RouteInfo object against the specified object and indicates if they are equal.
+ * @return {@code true} if the objects are equal, {@code false} otherwise.
+ */
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+
+ if (!(obj instanceof RouteInfo)) return false;
+
+ RouteInfo target = (RouteInfo) obj;
+
+ return Objects.equals(mDestination, target.getDestinationLinkAddress()) &&
+ Objects.equals(mGateway, target.getGateway()) &&
+ Objects.equals(mInterface, target.getInterface());
+ }
+
+ /**
+ * Returns a hashcode for this <code>RouteInfo</code> object.
+ */
+ public int hashCode() {
+ return (mDestination.hashCode() * 41)
+ + (mGateway == null ? 0 :mGateway.hashCode() * 47)
+ + (mInterface == null ? 0 :mInterface.hashCode() * 67);
+ }
+
+ /**
+ * Implement the Parcelable interface
+ */
public int describeContents() {
return 0;
}
+ /**
+ * Implement the Parcelable interface
+ */
public void writeToParcel(Parcel dest, int flags) {
- if (mDestination == null) {
- dest.writeByte((byte) 0);
- } else {
- dest.writeByte((byte) 1);
- dest.writeByteArray(mDestination.getAddress().getAddress());
- dest.writeInt(mDestination.getNetworkPrefixLength());
- }
+ dest.writeByteArray(mDestination.getAddress().getAddress());
+ dest.writeInt(mDestination.getPrefixLength());
if (mGateway == null) {
dest.writeByte((byte) 0);
@@ -208,38 +401,9 @@
dest.writeString(mInterface);
}
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
-
- if (!(obj instanceof RouteInfo)) return false;
-
- RouteInfo target = (RouteInfo) obj;
-
- boolean sameDestination = ( mDestination == null) ?
- target.getDestination() == null
- : mDestination.equals(target.getDestination());
-
- boolean sameAddress = (mGateway == null) ?
- target.getGateway() == null
- : mGateway.equals(target.getGateway());
-
- boolean sameInterface = (mInterface == null) ?
- target.getInterface() == null
- : mInterface.equals(target.getInterface());
-
- return sameDestination && sameAddress && sameInterface
- && mIsDefault == target.mIsDefault;
- }
-
- @Override
- public int hashCode() {
- return (mDestination == null ? 0 : mDestination.hashCode() * 41)
- + (mGateway == null ? 0 :mGateway.hashCode() * 47)
- + (mInterface == null ? 0 :mInterface.hashCode() * 67)
- + (mIsDefault ? 3 : 7);
- }
-
+ /**
+ * Implement the Parcelable interface.
+ */
public static final Creator<RouteInfo> CREATOR =
new Creator<RouteInfo>() {
public RouteInfo createFromParcel(Parcel in) {
@@ -247,17 +411,15 @@
int prefix = 0;
InetAddress gateway = null;
- if (in.readByte() == 1) {
- byte[] addr = in.createByteArray();
- prefix = in.readInt();
+ byte[] addr = in.createByteArray();
+ prefix = in.readInt();
- try {
- destAddr = InetAddress.getByAddress(addr);
- } catch (UnknownHostException e) {}
- }
+ try {
+ destAddr = InetAddress.getByAddress(addr);
+ } catch (UnknownHostException e) {}
if (in.readByte() == 1) {
- byte[] addr = in.createByteArray();
+ addr = in.createByteArray();
try {
gateway = InetAddress.getByAddress(addr);
@@ -279,39 +441,4 @@
return new RouteInfo[size];
}
};
-
- protected boolean matches(InetAddress destination) {
- if (destination == null) return false;
-
- // match the route destination and destination with prefix length
- InetAddress dstNet = NetworkUtils.getNetworkPart(destination,
- mDestination.getNetworkPrefixLength());
-
- return mDestination.getAddress().equals(dstNet);
- }
-
- /**
- * Find the route from a Collection of routes that best matches a given address.
- * May return null if no routes are applicable.
- * @param routes a Collection of RouteInfos to chose from
- * @param dest the InetAddress your trying to get to
- * @return the RouteInfo from the Collection that best fits the given address
- */
- public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
- if ((routes == null) || (dest == null)) return null;
-
- RouteInfo bestRoute = null;
- // pick a longest prefix match under same address type
- for (RouteInfo route : routes) {
- if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
- if ((bestRoute != null) &&
- (bestRoute.mDestination.getNetworkPrefixLength() >=
- route.mDestination.getNetworkPrefixLength())) {
- continue;
- }
- if (route.matches(dest)) bestRoute = route;
- }
- }
- return bestRoute;
- }
}
diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java
new file mode 100644
index 0000000..2e586b3
--- /dev/null
+++ b/core/java/android/net/UidRange.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014 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 static android.os.UserHandle.PER_USER_RANGE;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * An inclusive range of UIDs.
+ *
+ * @hide
+ */
+public final class UidRange implements Parcelable {
+ public final int start;
+ public final int stop;
+
+ public UidRange(int startUid, int stopUid) {
+ if (startUid < 0) throw new IllegalArgumentException("Invalid start UID.");
+ if (stopUid < 0) throw new IllegalArgumentException("Invalid stop UID.");
+ if (startUid > stopUid) throw new IllegalArgumentException("Invalid UID range.");
+ start = startUid;
+ stop = stopUid;
+ }
+
+ public static UidRange createForUser(int userId) {
+ return new UidRange(userId * PER_USER_RANGE, (userId + 1) * PER_USER_RANGE - 1);
+ }
+
+ public int getStartUser() {
+ return start / PER_USER_RANGE;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + start;
+ result = 31 * result + stop;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o instanceof UidRange) {
+ UidRange other = (UidRange) o;
+ return start == other.start && stop == other.stop;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return start + "-" + stop;
+ }
+
+ // implement the Parcelable interface
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(start);
+ dest.writeInt(stop);
+ }
+
+ public static final Creator<UidRange> CREATOR =
+ new Creator<UidRange>() {
+ @Override
+ public UidRange createFromParcel(Parcel in) {
+ int start = in.readInt();
+ int stop = in.readInt();
+
+ return new UidRange(start, stop);
+ }
+ @Override
+ public UidRange[] newArray(int size) {
+ return new UidRange[size];
+ }
+ };
+}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 6d23c32..2325bc7 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -18,6 +18,8 @@
#include "jni.h"
#include "JNIHelp.h"
+#include "NetdClient.h"
+#include "resolv_netid.h"
#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
@@ -212,7 +214,8 @@
return android_net_utils_runDhcpCommon(env, clazz, ifname, info, false);
}
-static jboolean android_net_utils_runDhcpRenew(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
+static jboolean android_net_utils_runDhcpRenew(JNIEnv* env, jobject clazz, jstring ifname,
+ jobject info)
{
return android_net_utils_runDhcpCommon(env, clazz, ifname, info, true);
}
@@ -250,6 +253,33 @@
}
}
+static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
+{
+ return (jboolean) !setNetworkForProcess(netId);
+}
+
+static jint android_net_utils_getNetworkBoundToProcess(JNIEnv *env, jobject thiz)
+{
+ return getNetworkForProcess();
+}
+
+static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz,
+ jint netId)
+{
+ return (jboolean) !setNetworkForResolv(netId);
+}
+
+static jboolean android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket,
+ jint netId)
+{
+ return (jboolean) !setNetworkForSocket(netId, socket);
+}
+
+static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket)
+{
+ return (jboolean) !protectFromVpn(socket);
+}
+
// ----------------------------------------------------------------------------
/*
@@ -267,6 +297,11 @@
{ "releaseDhcpLease", "(Ljava/lang/String;)Z", (void *)android_net_utils_releaseDhcpLease },
{ "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
{ "markSocket", "(II)V", (void*) android_net_utils_markSocket },
+ { "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
+ { "getNetworkBoundToProcess", "()I", (void*) android_net_utils_getNetworkBoundToProcess },
+ { "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
+ { "bindSocketToNetwork", "(II)Z", (void*) android_net_utils_bindSocketToNetwork },
+ { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
};
int register_android_net_NetworkUtils(JNIEnv* env)
diff --git a/core/tests/coretests/src/android/net/IpPrefixTest.java b/core/tests/coretests/src/android/net/IpPrefixTest.java
new file mode 100644
index 0000000..cf278fb
--- /dev/null
+++ b/core/tests/coretests/src/android/net/IpPrefixTest.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2014 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.IpPrefix;
+import android.os.Parcel;
+import static android.test.MoreAsserts.assertNotEqual;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static org.junit.Assert.assertArrayEquals;
+import java.net.InetAddress;
+import java.util.Random;
+import junit.framework.TestCase;
+
+
+public class IpPrefixTest extends TestCase {
+
+ // Explicitly cast everything to byte because "error: possible loss of precision".
+ private static final byte[] IPV4_BYTES = { (byte) 192, (byte) 0, (byte) 2, (byte) 4};
+ private static final byte[] IPV6_BYTES = {
+ (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+ (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
+ (byte) 0x0f, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xa0
+ };
+
+ @SmallTest
+ public void testConstructor() {
+ IpPrefix p;
+ try {
+ p = new IpPrefix((byte[]) null, 9);
+ fail("Expected NullPointerException: null byte array");
+ } catch(RuntimeException expected) {}
+
+ try {
+ p = new IpPrefix((InetAddress) null, 10);
+ fail("Expected NullPointerException: null InetAddress");
+ } catch(RuntimeException expected) {}
+
+ try {
+ p = new IpPrefix((String) null);
+ fail("Expected NullPointerException: null String");
+ } catch(RuntimeException expected) {}
+
+
+ try {
+ byte[] b2 = {1, 2, 3, 4, 5};
+ p = new IpPrefix(b2, 29);
+ fail("Expected IllegalArgumentException: invalid array length");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("1.2.3.4");
+ fail("Expected IllegalArgumentException: no prefix length");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("1.2.3.4/");
+ fail("Expected IllegalArgumentException: empty prefix length");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("foo/32");
+ fail("Expected IllegalArgumentException: invalid address");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("1/32");
+ fail("Expected IllegalArgumentException: deprecated IPv4 format");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("1.2.3.256/32");
+ fail("Expected IllegalArgumentException: invalid IPv4 address");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("foo/32");
+ fail("Expected IllegalArgumentException: non-address");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("f00:::/32");
+ fail("Expected IllegalArgumentException: invalid IPv6 address");
+ } catch(IllegalArgumentException expected) {}
+ }
+
+ public void testTruncation() {
+ IpPrefix p;
+
+ p = new IpPrefix(IPV4_BYTES, 32);
+ assertEquals("192.0.2.4/32", p.toString());
+
+ p = new IpPrefix(IPV4_BYTES, 29);
+ assertEquals("192.0.2.0/29", p.toString());
+
+ p = new IpPrefix(IPV4_BYTES, 8);
+ assertEquals("192.0.0.0/8", p.toString());
+
+ p = new IpPrefix(IPV4_BYTES, 0);
+ assertEquals("0.0.0.0/0", p.toString());
+
+ try {
+ p = new IpPrefix(IPV4_BYTES, 33);
+ fail("Expected IllegalArgumentException: invalid prefix length");
+ } catch(RuntimeException expected) {}
+
+ try {
+ p = new IpPrefix(IPV4_BYTES, 128);
+ fail("Expected IllegalArgumentException: invalid prefix length");
+ } catch(RuntimeException expected) {}
+
+ try {
+ p = new IpPrefix(IPV4_BYTES, -1);
+ fail("Expected IllegalArgumentException: negative prefix length");
+ } catch(RuntimeException expected) {}
+
+ p = new IpPrefix(IPV6_BYTES, 128);
+ assertEquals("2001:db8:dead:beef:f00::a0/128", p.toString());
+
+ p = new IpPrefix(IPV6_BYTES, 122);
+ assertEquals("2001:db8:dead:beef:f00::80/122", p.toString());
+
+ p = new IpPrefix(IPV6_BYTES, 64);
+ assertEquals("2001:db8:dead:beef::/64", p.toString());
+
+ p = new IpPrefix(IPV6_BYTES, 3);
+ assertEquals("2000::/3", p.toString());
+
+ p = new IpPrefix(IPV6_BYTES, 0);
+ assertEquals("::/0", p.toString());
+
+ try {
+ p = new IpPrefix(IPV6_BYTES, -1);
+ fail("Expected IllegalArgumentException: negative prefix length");
+ } catch(RuntimeException expected) {}
+
+ try {
+ p = new IpPrefix(IPV6_BYTES, 129);
+ fail("Expected IllegalArgumentException: negative prefix length");
+ } catch(RuntimeException expected) {}
+
+ }
+
+ private void assertAreEqual(Object o1, Object o2) {
+ assertTrue(o1.equals(o2));
+ assertTrue(o2.equals(o1));
+ }
+
+ private void assertAreNotEqual(Object o1, Object o2) {
+ assertFalse(o1.equals(o2));
+ assertFalse(o2.equals(o1));
+ }
+
+ @SmallTest
+ public void testEquals() {
+ IpPrefix p1, p2;
+
+ p1 = new IpPrefix("192.0.2.251/23");
+ p2 = new IpPrefix(new byte[]{(byte) 192, (byte) 0, (byte) 2, (byte) 251}, 23);
+ assertAreEqual(p1, p2);
+
+ p1 = new IpPrefix("192.0.2.5/23");
+ assertAreEqual(p1, p2);
+
+ p1 = new IpPrefix("192.0.2.5/24");
+ assertAreNotEqual(p1, p2);
+
+ p1 = new IpPrefix("192.0.4.5/23");
+ assertAreNotEqual(p1, p2);
+
+
+ p1 = new IpPrefix("2001:db8:dead:beef:f00::80/122");
+ p2 = new IpPrefix(IPV6_BYTES, 122);
+ assertEquals("2001:db8:dead:beef:f00::80/122", p2.toString());
+ assertAreEqual(p1, p2);
+
+ p1 = new IpPrefix("2001:db8:dead:beef:f00::bf/122");
+ assertAreEqual(p1, p2);
+
+ p1 = new IpPrefix("2001:db8:dead:beef:f00::8:0/123");
+ assertAreNotEqual(p1, p2);
+
+ p1 = new IpPrefix("2001:db8:dead:beef::/122");
+ assertAreNotEqual(p1, p2);
+
+ // 192.0.2.4/32 != c000:0204::/32.
+ byte[] ipv6bytes = new byte[16];
+ System.arraycopy(IPV4_BYTES, 0, ipv6bytes, 0, IPV4_BYTES.length);
+ p1 = new IpPrefix(ipv6bytes, 32);
+ assertAreEqual(p1, new IpPrefix("c000:0204::/32"));
+
+ p2 = new IpPrefix(IPV4_BYTES, 32);
+ assertAreNotEqual(p1, p2);
+ }
+
+ @SmallTest
+ public void testHashCode() {
+ IpPrefix p;
+ int oldCode = -1;
+ Random random = new Random();
+ for (int i = 0; i < 100; i++) {
+ if (random.nextBoolean()) {
+ // IPv4.
+ byte[] b = new byte[4];
+ random.nextBytes(b);
+ p = new IpPrefix(b, random.nextInt(33));
+ assertNotEqual(oldCode, p.hashCode());
+ oldCode = p.hashCode();
+ } else {
+ // IPv6.
+ byte[] b = new byte[16];
+ random.nextBytes(b);
+ p = new IpPrefix(b, random.nextInt(129));
+ assertNotEqual(oldCode, p.hashCode());
+ oldCode = p.hashCode();
+ }
+ }
+ }
+
+ @SmallTest
+ public void testMappedAddressesAreBroken() {
+ // 192.0.2.0/24 != ::ffff:c000:0204/120, but because we use InetAddress,
+ // we are unable to comprehend that.
+ byte[] ipv6bytes = {
+ (byte) 0, (byte) 0, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 0xff, (byte) 0xff,
+ (byte) 192, (byte) 0, (byte) 2, (byte) 0};
+ IpPrefix p = new IpPrefix(ipv6bytes, 120);
+ assertEquals(16, p.getRawAddress().length); // Fine.
+ assertArrayEquals(ipv6bytes, p.getRawAddress()); // Fine.
+
+ // Broken.
+ assertEquals("192.0.2.0/120", p.toString());
+ assertEquals(InetAddress.parseNumericAddress("192.0.2.0"), p.getAddress());
+ }
+
+ public IpPrefix passThroughParcel(IpPrefix p) {
+ Parcel parcel = Parcel.obtain();
+ IpPrefix p2 = null;
+ try {
+ p.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ p2 = IpPrefix.CREATOR.createFromParcel(parcel);
+ } finally {
+ parcel.recycle();
+ }
+ assertNotNull(p2);
+ return p2;
+ }
+
+ public void assertParcelingIsLossless(IpPrefix p) {
+ IpPrefix p2 = passThroughParcel(p);
+ assertEquals(p, p2);
+ }
+
+ public void testParceling() {
+ IpPrefix p;
+
+ p = new IpPrefix("2001:4860:db8::/64");
+ assertParcelingIsLossless(p);
+
+ p = new IpPrefix("192.0.2.0/25");
+ assertParcelingIsLossless(p);
+ }
+}
diff --git a/core/tests/coretests/src/android/net/LinkAddressTest.java b/core/tests/coretests/src/android/net/LinkAddressTest.java
index bccf556..7bc3974 100644
--- a/core/tests/coretests/src/android/net/LinkAddressTest.java
+++ b/core/tests/coretests/src/android/net/LinkAddressTest.java
@@ -30,6 +30,7 @@
import android.net.LinkAddress;
import android.os.Parcel;
import android.test.AndroidTestCase;
+import static android.test.MoreAsserts.assertNotEqual;
import android.test.suitebuilder.annotation.SmallTest;
import static android.system.OsConstants.IFA_F_DEPRECATED;
@@ -50,32 +51,43 @@
private static final InetAddress V4_ADDRESS = NetworkUtils.numericToInetAddress(V4);
private static final InetAddress V6_ADDRESS = NetworkUtils.numericToInetAddress(V6);
+ public void testConstants() {
+ // RT_SCOPE_UNIVERSE = 0, but all the other constants should be nonzero.
+ assertNotEqual(0, RT_SCOPE_HOST);
+ assertNotEqual(0, RT_SCOPE_LINK);
+ assertNotEqual(0, RT_SCOPE_SITE);
+
+ assertNotEqual(0, IFA_F_DEPRECATED);
+ assertNotEqual(0, IFA_F_PERMANENT);
+ assertNotEqual(0, IFA_F_TENTATIVE);
+ }
+
public void testConstructors() throws SocketException {
LinkAddress address;
// Valid addresses work as expected.
address = new LinkAddress(V4_ADDRESS, 25);
assertEquals(V4_ADDRESS, address.getAddress());
- assertEquals(25, address.getNetworkPrefixLength());
+ assertEquals(25, address.getPrefixLength());
assertEquals(0, address.getFlags());
assertEquals(RT_SCOPE_UNIVERSE, address.getScope());
address = new LinkAddress(V6_ADDRESS, 127);
assertEquals(V6_ADDRESS, address.getAddress());
- assertEquals(127, address.getNetworkPrefixLength());
+ assertEquals(127, address.getPrefixLength());
assertEquals(0, address.getFlags());
assertEquals(RT_SCOPE_UNIVERSE, address.getScope());
// Nonsensical flags/scopes or combinations thereof are acceptable.
address = new LinkAddress(V6 + "/64", IFA_F_DEPRECATED | IFA_F_PERMANENT, RT_SCOPE_LINK);
assertEquals(V6_ADDRESS, address.getAddress());
- assertEquals(64, address.getNetworkPrefixLength());
+ assertEquals(64, address.getPrefixLength());
assertEquals(IFA_F_DEPRECATED | IFA_F_PERMANENT, address.getFlags());
assertEquals(RT_SCOPE_LINK, address.getScope());
address = new LinkAddress(V4 + "/23", 123, 456);
assertEquals(V4_ADDRESS, address.getAddress());
- assertEquals(23, address.getNetworkPrefixLength());
+ assertEquals(23, address.getPrefixLength());
assertEquals(123, address.getFlags());
assertEquals(456, address.getScope());
@@ -94,10 +106,10 @@
}
assertEquals(NetworkUtils.numericToInetAddress("127.0.0.1"), ipv4Loopback.getAddress());
- assertEquals(8, ipv4Loopback.getNetworkPrefixLength());
+ assertEquals(8, ipv4Loopback.getPrefixLength());
assertEquals(NetworkUtils.numericToInetAddress("::1"), ipv6Loopback.getAddress());
- assertEquals(128, ipv6Loopback.getNetworkPrefixLength());
+ assertEquals(128, ipv6Loopback.getPrefixLength());
// Null addresses are rejected.
try {
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index 553afe0..4015b3d 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -38,6 +38,7 @@
private static LinkAddress LINKADDRV4 = new LinkAddress(ADDRV4, 32);
private static LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128);
+ private static LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64");
public void assertLinkPropertiesEqual(LinkProperties source, LinkProperties target) {
// Check implementation of equals(), element by element.
@@ -88,8 +89,8 @@
source.addLinkAddress(LINKADDRV4);
source.addLinkAddress(LINKADDRV6);
// set 2 dnses
- source.addDns(DNS1);
- source.addDns(DNS2);
+ source.addDnsServer(DNS1);
+ source.addDnsServer(DNS2);
// set 2 gateways
source.addRoute(new RouteInfo(GATEWAY1));
source.addRoute(new RouteInfo(GATEWAY2));
@@ -101,8 +102,8 @@
target.setInterfaceName(NAME);
target.addLinkAddress(LINKADDRV4);
target.addLinkAddress(LINKADDRV6);
- target.addDns(DNS1);
- target.addDns(DNS2);
+ target.addDnsServer(DNS1);
+ target.addDnsServer(DNS2);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
@@ -114,8 +115,8 @@
target.setInterfaceName("qmi1");
target.addLinkAddress(LINKADDRV4);
target.addLinkAddress(LINKADDRV6);
- target.addDns(DNS1);
- target.addDns(DNS2);
+ target.addDnsServer(DNS1);
+ target.addDnsServer(DNS2);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
@@ -127,8 +128,8 @@
target.addLinkAddress(new LinkAddress(
NetworkUtils.numericToInetAddress("75.208.6.2"), 32));
target.addLinkAddress(LINKADDRV6);
- target.addDns(DNS1);
- target.addDns(DNS2);
+ target.addDnsServer(DNS1);
+ target.addDnsServer(DNS2);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
@@ -139,8 +140,8 @@
target.addLinkAddress(LINKADDRV4);
target.addLinkAddress(LINKADDRV6);
// change dnses
- target.addDns(NetworkUtils.numericToInetAddress("75.208.7.2"));
- target.addDns(DNS2);
+ target.addDnsServer(NetworkUtils.numericToInetAddress("75.208.7.2"));
+ target.addDnsServer(DNS2);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
@@ -150,8 +151,8 @@
target.setInterfaceName(NAME);
target.addLinkAddress(LINKADDRV4);
target.addLinkAddress(LINKADDRV6);
- target.addDns(DNS1);
- target.addDns(DNS2);
+ target.addDnsServer(DNS1);
+ target.addDnsServer(DNS2);
// change gateway
target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress("75.208.8.2")));
target.addRoute(new RouteInfo(GATEWAY2));
@@ -162,8 +163,8 @@
target.setInterfaceName(NAME);
target.addLinkAddress(LINKADDRV4);
target.addLinkAddress(LINKADDRV6);
- target.addDns(DNS1);
- target.addDns(DNS2);
+ target.addDnsServer(DNS1);
+ target.addDnsServer(DNS2);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
// change mtu
@@ -185,8 +186,8 @@
source.addLinkAddress(LINKADDRV4);
source.addLinkAddress(LINKADDRV6);
// set 2 dnses
- source.addDns(DNS1);
- source.addDns(DNS2);
+ source.addDnsServer(DNS1);
+ source.addDnsServer(DNS2);
// set 2 gateways
source.addRoute(new RouteInfo(GATEWAY1));
source.addRoute(new RouteInfo(GATEWAY2));
@@ -197,8 +198,8 @@
target.setInterfaceName(NAME);
target.addLinkAddress(LINKADDRV6);
target.addLinkAddress(LINKADDRV4);
- target.addDns(DNS2);
- target.addDns(DNS1);
+ target.addDnsServer(DNS2);
+ target.addDnsServer(DNS1);
target.addRoute(new RouteInfo(GATEWAY2));
target.addRoute(new RouteInfo(GATEWAY1));
target.setMtu(MTU);
@@ -245,11 +246,15 @@
// Add a route with no interface to a LinkProperties with no interface. No errors.
LinkProperties lp = new LinkProperties();
RouteInfo r = new RouteInfo(prefix, address, null);
- lp.addRoute(r);
+ assertTrue(lp.addRoute(r));
assertEquals(1, lp.getRoutes().size());
assertAllRoutesHaveInterface(null, lp);
- // Add a route with an interface. Except an exception.
+ // Adding the same route twice has no effect.
+ assertFalse(lp.addRoute(r));
+ assertEquals(1, lp.getRoutes().size());
+
+ // Add a route with an interface. Expect an exception.
r = new RouteInfo(prefix, address, "wlan0");
try {
lp.addRoute(r);
@@ -267,6 +272,7 @@
} catch (IllegalArgumentException expected) {}
// If the interface name matches, the route is added.
+ r = new RouteInfo(prefix, null, "wlan0");
lp.setInterfaceName("wlan0");
lp.addRoute(r);
assertEquals(2, lp.getRoutes().size());
@@ -352,7 +358,7 @@
// No addresses.
assertFalse(lp.hasIPv4Address());
- assertFalse(lp.hasIPv6Address());
+ assertFalse(lp.hasGlobalIPv6Address());
// Addresses on stacked links don't count.
LinkProperties stacked = new LinkProperties();
@@ -361,12 +367,12 @@
stacked.addLinkAddress(LINKADDRV4);
stacked.addLinkAddress(LINKADDRV6);
assertTrue(stacked.hasIPv4Address());
- assertTrue(stacked.hasIPv6Address());
+ assertTrue(stacked.hasGlobalIPv6Address());
assertFalse(lp.hasIPv4Address());
- assertFalse(lp.hasIPv6Address());
+ assertFalse(lp.hasGlobalIPv6Address());
lp.removeStackedLink(stacked);
assertFalse(lp.hasIPv4Address());
- assertFalse(lp.hasIPv6Address());
+ assertFalse(lp.hasGlobalIPv6Address());
// Addresses on the base link.
// Check the return values of hasIPvXAddress and ensure the add/remove methods return true
@@ -375,19 +381,29 @@
assertTrue(lp.addLinkAddress(LINKADDRV6));
assertEquals(1, lp.getLinkAddresses().size());
assertFalse(lp.hasIPv4Address());
- assertTrue(lp.hasIPv6Address());
+ assertTrue(lp.hasGlobalIPv6Address());
assertTrue(lp.removeLinkAddress(LINKADDRV6));
assertEquals(0, lp.getLinkAddresses().size());
- assertTrue(lp.addLinkAddress(LINKADDRV4));
- assertEquals(1, lp.getLinkAddresses().size());
- assertTrue(lp.hasIPv4Address());
- assertFalse(lp.hasIPv6Address());
- assertTrue(lp.addLinkAddress(LINKADDRV6));
+ assertTrue(lp.addLinkAddress(LINKADDRV6LINKLOCAL));
+ assertEquals(1, lp.getLinkAddresses().size());
+ assertFalse(lp.hasGlobalIPv6Address());
+
+ assertTrue(lp.addLinkAddress(LINKADDRV4));
assertEquals(2, lp.getLinkAddresses().size());
assertTrue(lp.hasIPv4Address());
- assertTrue(lp.hasIPv6Address());
+ assertFalse(lp.hasGlobalIPv6Address());
+
+ assertTrue(lp.addLinkAddress(LINKADDRV6));
+ assertEquals(3, lp.getLinkAddresses().size());
+ assertTrue(lp.hasIPv4Address());
+ assertTrue(lp.hasGlobalIPv6Address());
+
+ assertTrue(lp.removeLinkAddress(LINKADDRV6LINKLOCAL));
+ assertEquals(2, lp.getLinkAddresses().size());
+ assertTrue(lp.hasIPv4Address());
+ assertTrue(lp.hasGlobalIPv6Address());
// Adding an address twice has no effect.
// Removing an address that's not present has no effect.
diff --git a/core/tests/coretests/src/android/net/RouteInfoTest.java b/core/tests/coretests/src/android/net/RouteInfoTest.java
index 55d6592..dcacd11 100644
--- a/core/tests/coretests/src/android/net/RouteInfoTest.java
+++ b/core/tests/coretests/src/android/net/RouteInfoTest.java
@@ -19,7 +19,7 @@
import java.lang.reflect.Method;
import java.net.InetAddress;
-import android.net.LinkAddress;
+import android.net.IpPrefix;
import android.net.RouteInfo;
import android.os.Parcel;
@@ -32,9 +32,8 @@
return InetAddress.parseNumericAddress(addr);
}
- private LinkAddress Prefix(String prefix) {
- String[] parts = prefix.split("/");
- return new LinkAddress(Address(parts[0]), Integer.parseInt(parts[1]));
+ private IpPrefix Prefix(String prefix) {
+ return new IpPrefix(prefix);
}
@SmallTest
@@ -43,17 +42,17 @@
// Invalid input.
try {
- r = new RouteInfo(null, null, "rmnet0");
+ r = new RouteInfo((IpPrefix) null, null, "rmnet0");
fail("Expected RuntimeException: destination and gateway null");
} catch(RuntimeException e) {}
// Null destination is default route.
- r = new RouteInfo(null, Address("2001:db8::1"), null);
+ r = new RouteInfo((IpPrefix) null, Address("2001:db8::1"), null);
assertEquals(Prefix("::/0"), r.getDestination());
assertEquals(Address("2001:db8::1"), r.getGateway());
assertNull(r.getInterface());
- r = new RouteInfo(null, Address("192.0.2.1"), "wlan0");
+ r = new RouteInfo((IpPrefix) null, Address("192.0.2.1"), "wlan0");
assertEquals(Prefix("0.0.0.0/0"), r.getDestination());
assertEquals(Address("192.0.2.1"), r.getGateway());
assertEquals("wlan0", r.getInterface());
@@ -71,17 +70,19 @@
}
public void testMatches() {
- class PatchedRouteInfo extends RouteInfo {
- public PatchedRouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
- super(destination, gateway, iface);
+ class PatchedRouteInfo {
+ private final RouteInfo mRouteInfo;
+
+ public PatchedRouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
+ mRouteInfo = new RouteInfo(destination, gateway, iface);
}
public boolean matches(InetAddress destination) {
- return super.matches(destination);
+ return mRouteInfo.matches(destination);
}
}
- RouteInfo r;
+ PatchedRouteInfo r;
r = new PatchedRouteInfo(Prefix("2001:db8:f00::ace:d00d/127"), null, "rmnet0");
assertTrue(r.matches(Address("2001:db8:f00::ace:d00c")));
@@ -96,11 +97,11 @@
assertFalse(r.matches(Address("192.0.0.21")));
assertFalse(r.matches(Address("8.8.8.8")));
- RouteInfo ipv6Default = new PatchedRouteInfo(Prefix("::/0"), null, "rmnet0");
+ PatchedRouteInfo ipv6Default = new PatchedRouteInfo(Prefix("::/0"), null, "rmnet0");
assertTrue(ipv6Default.matches(Address("2001:db8::f00")));
assertFalse(ipv6Default.matches(Address("192.0.2.1")));
- RouteInfo ipv4Default = new PatchedRouteInfo(Prefix("0.0.0.0/0"), null, "rmnet0");
+ PatchedRouteInfo ipv4Default = new PatchedRouteInfo(Prefix("0.0.0.0/0"), null, "rmnet0");
assertTrue(ipv4Default.matches(Address("255.255.255.255")));
assertTrue(ipv4Default.matches(Address("192.0.2.1")));
assertFalse(ipv4Default.matches(Address("2001:db8::f00")));
@@ -149,38 +150,68 @@
assertAreNotEqual(r1, r3);
}
- public void testHostRoute() {
+ public void testHostAndDefaultRoutes() {
RouteInfo r;
r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0");
assertFalse(r.isHostRoute());
+ assertTrue(r.isDefaultRoute());
+ assertTrue(r.isIPv4Default());
+ assertFalse(r.isIPv6Default());
r = new RouteInfo(Prefix("::/0"), Address("::"), "wlan0");
assertFalse(r.isHostRoute());
+ assertTrue(r.isDefaultRoute());
+ assertFalse(r.isIPv4Default());
+ assertTrue(r.isIPv6Default());
r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0");
assertFalse(r.isHostRoute());
+ assertFalse(r.isDefaultRoute());
+ assertFalse(r.isIPv4Default());
+ assertFalse(r.isIPv6Default());
r = new RouteInfo(Prefix("2001:db8::/48"), null, "wlan0");
assertFalse(r.isHostRoute());
+ assertFalse(r.isDefaultRoute());
+ assertFalse(r.isIPv4Default());
+ assertFalse(r.isIPv6Default());
r = new RouteInfo(Prefix("192.0.2.0/32"), Address("0.0.0.0"), "wlan0");
assertTrue(r.isHostRoute());
+ assertFalse(r.isDefaultRoute());
+ assertFalse(r.isIPv4Default());
+ assertFalse(r.isIPv6Default());
r = new RouteInfo(Prefix("2001:db8::/128"), Address("::"), "wlan0");
assertTrue(r.isHostRoute());
+ assertFalse(r.isDefaultRoute());
+ assertFalse(r.isIPv4Default());
+ assertFalse(r.isIPv6Default());
r = new RouteInfo(Prefix("192.0.2.0/32"), null, "wlan0");
assertTrue(r.isHostRoute());
+ assertFalse(r.isDefaultRoute());
+ assertFalse(r.isIPv4Default());
+ assertFalse(r.isIPv6Default());
r = new RouteInfo(Prefix("2001:db8::/128"), null, "wlan0");
assertTrue(r.isHostRoute());
+ assertFalse(r.isDefaultRoute());
+ assertFalse(r.isIPv4Default());
+ assertFalse(r.isIPv6Default());
r = new RouteInfo(Prefix("::/128"), Address("fe80::"), "wlan0");
assertTrue(r.isHostRoute());
+ assertFalse(r.isDefaultRoute());
+ assertFalse(r.isIPv4Default());
+ assertFalse(r.isIPv6Default());
r = new RouteInfo(Prefix("0.0.0.0/32"), Address("192.0.2.1"), "wlan0");
assertTrue(r.isHostRoute());
+ assertFalse(r.isDefaultRoute());
+ assertFalse(r.isIPv4Default());
+ assertFalse(r.isIPv6Default());
}
public RouteInfo passThroughParcel(RouteInfo r) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index aa14da1..b52aecf 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -22,8 +22,16 @@
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
import static android.net.ConnectivityManager.TYPE_DUMMY;
-import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_IMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_CBS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_IA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.ConnectivityManager.TYPE_PROXY;
@@ -37,7 +45,6 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.bluetooth.BluetoothTetheringDataTracker;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -53,8 +60,6 @@
import android.database.ContentObserver;
import android.net.CaptivePortalTracker;
import android.net.ConnectivityManager;
-import android.net.DummyDataStateTracker;
-import android.net.EthernetDataTracker;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
@@ -65,20 +70,25 @@
import android.net.LinkProperties.CompareResult;
import android.net.LinkQualityInfo;
import android.net.MobileDataStateTracker;
+import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkFactory;
import android.net.NetworkQuotaInfo;
+import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.NetworkStateTracker;
import android.net.NetworkUtils;
import android.net.Proxy;
import android.net.ProxyDataTracker;
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.SamplingDataTracker;
+import android.net.UidRange;
import android.net.Uri;
-import android.net.wifi.WifiStateTracker;
import android.net.wimax.WimaxManagerConstants;
import android.os.AsyncTask;
import android.os.Binder;
@@ -99,6 +109,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyStore;
@@ -117,11 +128,15 @@
import com.android.internal.telephony.DctConstants;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.AsyncChannel;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.Nat464Xlat;
+import com.android.server.connectivity.NetworkAgentInfo;
+import com.android.server.connectivity.NetworkMonitor;
import com.android.server.connectivity.PacManager;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
@@ -147,7 +162,6 @@
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.URL;
-import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -172,7 +186,10 @@
private static final String TAG = "ConnectivityService";
private static final boolean DBG = true;
- private static final boolean VDBG = false;
+ private static final boolean VDBG = true; // STOPSHIP
+
+ // network sampling debugging
+ private static final boolean SAMPLE_DBG = false;
private static final boolean LOGD_RULES = false;
@@ -200,10 +217,10 @@
// Set network sampling interval at 12 minutes, this way, even if the timers get
// aggregated, it will fire at around 15 minutes, which should allow us to
// aggregate this timer with other timers (specially the socket keep alive timers)
- private static final int DEFAULT_SAMPLING_INTERVAL_IN_SECONDS = (VDBG ? 30 : 12 * 60);
+ private static final int DEFAULT_SAMPLING_INTERVAL_IN_SECONDS = (SAMPLE_DBG ? 30 : 12 * 60);
// start network sampling a minute after booting ...
- private static final int DEFAULT_START_SAMPLING_INTERVAL_IN_SECONDS = (VDBG ? 30 : 60);
+ private static final int DEFAULT_START_SAMPLING_INTERVAL_IN_SECONDS = (SAMPLE_DBG ? 30 : 60);
AlarmManager mAlarmManager;
@@ -217,7 +234,6 @@
@GuardedBy("mVpns")
private final SparseArray<Vpn> mVpns = new SparseArray<Vpn>();
- private VpnCallback mVpnCallback = new VpnCallback();
private boolean mLockdownEnabled;
private LockdownVpnTracker mLockdownTracker;
@@ -238,7 +254,10 @@
*/
private NetworkStateTracker mNetTrackers[];
- /* Handles captive portal check on a network */
+ /*
+ * Handles captive portal check on a network.
+ * Only set if device has {@link PackageManager#FEATURE_WIFI}.
+ */
private CaptivePortalTracker mCaptivePortalTracker;
/**
@@ -299,12 +318,6 @@
private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED = 2;
/**
- * used internally to change our network preference setting
- * arg1 = networkType to prefer
- */
- private static final int EVENT_SET_NETWORK_PREFERENCE = 3;
-
- /**
* used internally to synchronize inet condition reports
* arg1 = networkType
* arg2 = condition (0 bad, 100 good)
@@ -318,14 +331,10 @@
private static final int EVENT_INET_CONDITION_HOLD_END = 5;
/**
- * used internally to set enable/disable cellular data
- * arg1 = ENBALED or DISABLED
- */
- private static final int EVENT_SET_MOBILE_DATA = 7;
-
- /**
* used internally to clear a wakelock when transitioning
- * from one net to another
+ * from one net to another. Clear happens when we get a new
+ * network - EVENT_EXPIRE_NET_TRANSITION_WAKELOCK happens
+ * after a timeout if no network is found (typically 1 min).
*/
private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 8;
@@ -352,15 +361,13 @@
*/
private static final int EVENT_SET_POLICY_DATA_ENABLE = 12;
- private static final int EVENT_VPN_STATE_CHANGED = 13;
-
/**
* Used internally to disable fail fast of mobile data
*/
private static final int EVENT_ENABLE_FAIL_FAST_MOBILE_DATA = 14;
/**
- * user internally to indicate that data sampling interval is up
+ * used internally to indicate that data sampling interval is up
*/
private static final int EVENT_SAMPLE_INTERVAL_ELAPSED = 15;
@@ -369,10 +376,65 @@
*/
private static final int EVENT_PROXY_HAS_CHANGED = 16;
+ /**
+ * used internally when registering NetworkFactories
+ * obj = NetworkFactoryInfo
+ */
+ private static final int EVENT_REGISTER_NETWORK_FACTORY = 17;
+
+ /**
+ * used internally when registering NetworkAgents
+ * obj = Messenger
+ */
+ private static final int EVENT_REGISTER_NETWORK_AGENT = 18;
+
+ /**
+ * used to add a network request
+ * includes a NetworkRequestInfo
+ */
+ private static final int EVENT_REGISTER_NETWORK_REQUEST = 19;
+
+ /**
+ * indicates a timeout period is over - check if we had a network yet or not
+ * and if not, call the timeout calback (but leave the request live until they
+ * cancel it.
+ * includes a NetworkRequestInfo
+ */
+ private static final int EVENT_TIMEOUT_NETWORK_REQUEST = 20;
+
+ /**
+ * used to add a network listener - no request
+ * includes a NetworkRequestInfo
+ */
+ private static final int EVENT_REGISTER_NETWORK_LISTENER = 21;
+
+ /**
+ * used to remove a network request, either a listener or a real request
+ * arg1 = UID of caller
+ * obj = NetworkRequest
+ */
+ private static final int EVENT_RELEASE_NETWORK_REQUEST = 22;
+
+ /**
+ * used internally when registering NetworkFactories
+ * obj = Messenger
+ */
+ private static final int EVENT_UNREGISTER_NETWORK_FACTORY = 23;
+
+ /**
+ * used internally to expire a wakelock when transitioning
+ * from one net to another. Expire happens when we fail to find
+ * a new network (typically after 1 minute) -
+ * EVENT_CLEAR_NET_TRANSITION_WAKELOCK happens if we had found
+ * a replacement network.
+ */
+ private static final int EVENT_EXPIRE_NET_TRANSITION_WAKELOCK = 24;
+
+
/** Handler used for internal events. */
- private InternalHandler mHandler;
+ final private InternalHandler mHandler;
/** Handler used for incoming {@link NetworkStateTracker} events. */
- private NetworkStateTrackerHandler mTrackerHandler;
+ final private NetworkStateTrackerHandler mTrackerHandler;
// list of DeathRecipients used to make sure features are turned off when
// a process dies
@@ -406,12 +468,12 @@
private ArrayList mInetLog;
// track the current default http proxy - tell the world if we get a new one (real change)
- private ProxyProperties mDefaultProxy = null;
+ private ProxyInfo mDefaultProxy = null;
private Object mProxyLock = new Object();
private boolean mDefaultProxyDisabled = false;
// track the global proxy.
- private ProxyProperties mGlobalProxy = null;
+ private ProxyInfo mGlobalProxy = null;
private PacManager mPacManager = null;
@@ -419,6 +481,8 @@
private AppOpsManager mAppOpsManager;
+ private UserManager mUserManager;
+
NetworkConfig[] mNetConfigs;
int mNetworksDefined;
@@ -442,6 +506,128 @@
TelephonyManager mTelephonyManager;
+ // sequence number for Networks
+ private final static int MIN_NET_ID = 10; // some reserved marks
+ private final static int MAX_NET_ID = 65535;
+ private int mNextNetId = MIN_NET_ID;
+
+ // sequence number of NetworkRequests
+ private int mNextNetworkRequestId = 1;
+
+ private static final int UID_UNUSED = -1;
+
+ /**
+ * Implements support for the legacy "one network per network type" model.
+ *
+ * We used to have a static array of NetworkStateTrackers, one for each
+ * network type, but that doesn't work any more now that we can have,
+ * for example, more that one wifi network. This class stores all the
+ * NetworkAgentInfo objects that support a given type, but the legacy
+ * API will only see the first one.
+ *
+ * It serves two main purposes:
+ *
+ * 1. Provide information about "the network for a given type" (since this
+ * API only supports one).
+ * 2. Send legacy connectivity change broadcasts. Broadcasts are sent if
+ * the first network for a given type changes, or if the default network
+ * changes.
+ */
+ private class LegacyTypeTracker {
+ /**
+ * Array of lists, one per legacy network type (e.g., TYPE_MOBILE_MMS).
+ * Each list holds references to all NetworkAgentInfos that are used to
+ * satisfy requests for that network type.
+ *
+ * This array is built out at startup such that an unsupported network
+ * doesn't get an ArrayList instance, making this a tristate:
+ * unsupported, supported but not active and active.
+ *
+ * The actual lists are populated when we scan the network types that
+ * are supported on this device.
+ */
+ private ArrayList<NetworkAgentInfo> mTypeLists[];
+
+ public LegacyTypeTracker() {
+ mTypeLists = (ArrayList<NetworkAgentInfo>[])
+ new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
+ }
+
+ public void addSupportedType(int type) {
+ if (mTypeLists[type] != null) {
+ throw new IllegalStateException(
+ "legacy list for type " + type + "already initialized");
+ }
+ mTypeLists[type] = new ArrayList<NetworkAgentInfo>();
+ }
+
+ private boolean isDefaultNetwork(NetworkAgentInfo nai) {
+ return mNetworkForRequestId.get(mDefaultRequest.requestId) == nai;
+ }
+
+ public boolean isTypeSupported(int type) {
+ return isNetworkTypeValid(type) && mTypeLists[type] != null;
+ }
+
+ public NetworkAgentInfo getNetworkForType(int type) {
+ if (isTypeSupported(type) && !mTypeLists[type].isEmpty()) {
+ return mTypeLists[type].get(0);
+ } else {
+ return null;
+ }
+ }
+
+ public void add(int type, NetworkAgentInfo nai) {
+ if (!isTypeSupported(type)) {
+ return; // Invalid network type.
+ }
+ if (VDBG) log("Adding agent " + nai + " for legacy network type " + type);
+
+ ArrayList<NetworkAgentInfo> list = mTypeLists[type];
+ if (list.contains(nai)) {
+ loge("Attempting to register duplicate agent for type " + type + ": " + nai);
+ return;
+ }
+
+ if (list.isEmpty() || isDefaultNetwork(nai)) {
+ if (VDBG) log("Sending connected broadcast for type " + type +
+ "isDefaultNetwork=" + isDefaultNetwork(nai));
+ sendLegacyNetworkBroadcast(nai, true, type);
+ }
+ list.add(nai);
+ }
+
+ public void remove(NetworkAgentInfo nai) {
+ if (VDBG) log("Removing agent " + nai);
+ for (int type = 0; type < mTypeLists.length; type++) {
+ ArrayList<NetworkAgentInfo> list = mTypeLists[type];
+ if (list == null || list.isEmpty()) {
+ continue;
+ }
+
+ boolean wasFirstNetwork = false;
+ if (list.get(0).equals(nai)) {
+ // This network was the first in the list. Send broadcast.
+ wasFirstNetwork = true;
+ }
+ list.remove(nai);
+
+ if (wasFirstNetwork || isDefaultNetwork(nai)) {
+ if (VDBG) log("Sending disconnected broadcast for type " + type +
+ "isDefaultNetwork=" + isDefaultNetwork(nai));
+ sendLegacyNetworkBroadcast(nai, false, type);
+ }
+
+ if (!list.isEmpty() && wasFirstNetwork) {
+ if (VDBG) log("Other network available for type " + type +
+ ", sending connected broadcast");
+ sendLegacyNetworkBroadcast(list.get(0), false, type);
+ }
+ }
+ }
+ }
+ private LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker();
+
public ConnectivityService(Context context, INetworkManagementService netd,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
// Currently, omitting a NetworkFactory will create one internally
@@ -454,6 +640,14 @@
NetworkFactory netFactory) {
if (DBG) log("ConnectivityService starting up");
+ NetworkCapabilities netCap = new NetworkCapabilities();
+ netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ mDefaultRequest = new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId());
+ NetworkRequestInfo nri = new NetworkRequestInfo(null, mDefaultRequest, new Binder(),
+ NetworkRequestInfo.REQUEST);
+ mNetworkRequests.put(mDefaultRequest, nri);
+
HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
handlerThread.start();
mHandler = new InternalHandler(handlerThread.getLooper());
@@ -559,6 +753,8 @@
"radio " + n.radio + " in network type " + n.type);
continue;
}
+ mLegacyTypeTracker.addSupportedType(n.type);
+
mNetConfigs[n.type] = n;
mNetworksDefined++;
} catch(Exception e) {
@@ -601,21 +797,6 @@
}
}
- // Update mNetworkPreference according to user mannually first then overlay config.xml
- mNetworkPreference = getPersistedNetworkPreference();
- if (mNetworkPreference == -1) {
- for (int n : mPriorityList) {
- if (mNetConfigs[n].isDefault() && ConnectivityManager.isNetworkTypeValid(n)) {
- mNetworkPreference = n;
- break;
- }
- }
- if (mNetworkPreference == -1) {
- throw new IllegalStateException(
- "You should set at least one default Network in config.xml!");
- }
- }
-
mNetRequestersPids =
(List<Integer> [])new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1];
for (int i : mPriorityList) {
@@ -704,11 +885,27 @@
mContext.registerReceiver(mProvisioningReceiver, filter);
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ }
+
+ private synchronized int nextNetworkRequestId() {
+ return mNextNetworkRequestId++;
+ }
+
+ private synchronized int nextNetId() {
+ int netId = mNextNetId;
+ if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
+ return netId;
}
/**
* Factory that creates {@link NetworkStateTracker} instances using given
* {@link NetworkConfig}.
+ *
+ * TODO - this is obsolete and will be deleted. It's replaced by the
+ * registerNetworkFactory call and protocol.
+ * @Deprecated in favor of registerNetworkFactory dynamic bindings
*/
public interface NetworkFactory {
public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config);
@@ -726,18 +923,8 @@
@Override
public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config) {
switch (config.radio) {
- case TYPE_WIFI:
- return new WifiStateTracker(targetNetworkType, config.name);
- case TYPE_MOBILE:
- return new MobileDataStateTracker(targetNetworkType, config.name);
- case TYPE_DUMMY:
- return new DummyDataStateTracker(targetNetworkType, config.name);
- case TYPE_BLUETOOTH:
- return BluetoothTetheringDataTracker.getInstance();
case TYPE_WIMAX:
return makeWimaxStateTracker(mContext, mTrackerHandler);
- case TYPE_ETHERNET:
- return EthernetDataTracker.getInstance();
case TYPE_PROXY:
return new ProxyDataTracker();
default:
@@ -830,41 +1017,6 @@
return wimaxStateTracker;
}
- /**
- * Sets the preferred network.
- * @param preference the new preference
- */
- public void setNetworkPreference(int preference) {
- enforceChangePermission();
-
- mHandler.sendMessage(
- mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0));
- }
-
- public int getNetworkPreference() {
- enforceAccessPermission();
- int preference;
- synchronized(this) {
- preference = mNetworkPreference;
- }
- return preference;
- }
-
- private void handleSetNetworkPreference(int preference) {
- if (ConnectivityManager.isNetworkTypeValid(preference) &&
- mNetConfigs[preference] != null &&
- mNetConfigs[preference].isDefault()) {
- if (mNetworkPreference != preference) {
- final ContentResolver cr = mContext.getContentResolver();
- Settings.Global.putInt(cr, Settings.Global.NETWORK_PREFERENCE, preference);
- synchronized(this) {
- mNetworkPreference = preference;
- }
- enforcePreference();
- }
- }
- }
-
private int getConnectivityChangeDelay() {
final ContentResolver cr = mContext.getContentResolver();
@@ -876,41 +1028,6 @@
defaultDelay);
}
- private int getPersistedNetworkPreference() {
- final ContentResolver cr = mContext.getContentResolver();
-
- final int networkPrefSetting = Settings.Global
- .getInt(cr, Settings.Global.NETWORK_PREFERENCE, -1);
-
- return networkPrefSetting;
- }
-
- /**
- * 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 (mNetTrackers[mNetworkPreference].getNetworkInfo().isConnected())
- return;
-
- if (!mNetTrackers[mNetworkPreference].isAvailable())
- return;
-
- for (int t=0; t <= ConnectivityManager.MAX_RADIO_TYPE; t++) {
- if (t != mNetworkPreference && mNetTrackers[t] != null &&
- mNetTrackers[t].getNetworkInfo().isConnected()) {
- if (DBG) {
- log("tearing down " + mNetTrackers[t].getNetworkInfo() +
- " in enforcePreference");
- }
- teardown(mNetTrackers[t]);
- }
- }
- }
-
private boolean teardown(NetworkStateTracker netTracker) {
if (netTracker.teardown()) {
netTracker.setTeardownRequested(true);
@@ -924,11 +1041,12 @@
* Check if UID should be blocked from using the network represented by the
* given {@link NetworkStateTracker}.
*/
- private boolean isNetworkBlocked(NetworkStateTracker tracker, int uid) {
- final String iface = tracker.getLinkProperties().getInterfaceName();
-
+ private boolean isNetworkBlocked(int networkType, int uid) {
final boolean networkCostly;
final int uidRules;
+
+ LinkProperties lp = getLinkPropertiesForType(networkType);
+ final String iface = (lp == null ? "" : lp.getInterfaceName());
synchronized (mRulesLock) {
networkCostly = mMeteredIfaces.contains(iface);
uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
@@ -945,11 +1063,15 @@
/**
* Return a filtered {@link NetworkInfo}, potentially marked
* {@link DetailedState#BLOCKED} based on
- * {@link #isNetworkBlocked(NetworkStateTracker, int)}.
+ * {@link #isNetworkBlocked}.
*/
- private NetworkInfo getFilteredNetworkInfo(NetworkStateTracker tracker, int uid) {
- NetworkInfo info = tracker.getNetworkInfo();
- if (isNetworkBlocked(tracker, uid)) {
+ private NetworkInfo getFilteredNetworkInfo(int networkType, int uid) {
+ NetworkInfo info = getNetworkInfoForType(networkType);
+ return getFilteredNetworkInfo(info, networkType, uid);
+ }
+
+ private NetworkInfo getFilteredNetworkInfo(NetworkInfo info, int networkType, int uid) {
+ if (isNetworkBlocked(networkType, uid)) {
// network is blocked; clone and override state
info = new NetworkInfo(info);
info.setDetailedState(DetailedState.BLOCKED, null, null);
@@ -974,6 +1096,15 @@
return getNetworkInfo(mActiveDefaultNetwork, uid);
}
+ // only called when the default request is satisfied
+ private void updateActiveDefaultNetwork(NetworkAgentInfo nai) {
+ if (nai != null) {
+ mActiveDefaultNetwork = nai.networkInfo.getType();
+ } else {
+ mActiveDefaultNetwork = TYPE_NONE;
+ }
+ }
+
/**
* Find the first Provisioning network.
*
@@ -1016,10 +1147,7 @@
public NetworkInfo getActiveNetworkInfoUnfiltered() {
enforceAccessPermission();
if (isNetworkTypeValid(mActiveDefaultNetwork)) {
- final NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork];
- if (tracker != null) {
- return tracker.getNetworkInfo();
- }
+ return getNetworkInfoForType(mActiveDefaultNetwork);
}
return null;
}
@@ -1040,23 +1168,41 @@
private NetworkInfo getNetworkInfo(int networkType, int uid) {
NetworkInfo info = null;
if (isNetworkTypeValid(networkType)) {
- final NetworkStateTracker tracker = mNetTrackers[networkType];
- if (tracker != null) {
- info = getFilteredNetworkInfo(tracker, uid);
+ if (getNetworkInfoForType(networkType) != null) {
+ info = getFilteredNetworkInfo(networkType, uid);
}
}
return info;
}
@Override
+ public NetworkInfo getNetworkInfoForNetwork(Network network) {
+ enforceAccessPermission();
+ if (network == null) return null;
+
+ final int uid = Binder.getCallingUid();
+ NetworkAgentInfo nai = null;
+ synchronized (mNetworkForNetId) {
+ nai = mNetworkForNetId.get(network.netId);
+ }
+ if (nai == null) return null;
+ synchronized (nai) {
+ if (nai.networkInfo == null) return null;
+
+ return getFilteredNetworkInfo(nai.networkInfo, nai.networkInfo.getType(), uid);
+ }
+ }
+
+ @Override
public NetworkInfo[] getAllNetworkInfo() {
enforceAccessPermission();
final int uid = Binder.getCallingUid();
final ArrayList<NetworkInfo> result = Lists.newArrayList();
synchronized (mRulesLock) {
- for (NetworkStateTracker tracker : mNetTrackers) {
- if (tracker != null) {
- result.add(getFilteredNetworkInfo(tracker, uid));
+ for (int networkType = 0; networkType <= ConnectivityManager.MAX_NETWORK_TYPE;
+ networkType++) {
+ if (getNetworkInfoForType(networkType) != null) {
+ result.add(getFilteredNetworkInfo(networkType, uid));
}
}
}
@@ -1064,9 +1210,21 @@
}
@Override
+ public Network[] getAllNetworks() {
+ enforceAccessPermission();
+ final ArrayList<Network> result = new ArrayList();
+ synchronized (mNetworkForNetId) {
+ for (int i = 0; i < mNetworkForNetId.size(); i++) {
+ result.add(new Network(mNetworkForNetId.valueAt(i).network));
+ }
+ }
+ return result.toArray(new Network[result.size()]);
+ }
+
+ @Override
public boolean isNetworkSupported(int networkType) {
enforceAccessPermission();
- return (isNetworkTypeValid(networkType) && (mNetTrackers[networkType] != null));
+ return (isNetworkTypeValid(networkType) && (getNetworkInfoForType(networkType) != null));
}
/**
@@ -1079,16 +1237,45 @@
*/
@Override
public LinkProperties getActiveLinkProperties() {
- return getLinkProperties(mActiveDefaultNetwork);
+ return getLinkPropertiesForType(mActiveDefaultNetwork);
}
@Override
- public LinkProperties getLinkProperties(int networkType) {
+ public LinkProperties getLinkPropertiesForType(int networkType) {
enforceAccessPermission();
if (isNetworkTypeValid(networkType)) {
- final NetworkStateTracker tracker = mNetTrackers[networkType];
- if (tracker != null) {
- return tracker.getLinkProperties();
+ return getLinkPropertiesForTypeInternal(networkType);
+ }
+ return null;
+ }
+
+ // TODO - this should be ALL networks
+ @Override
+ public LinkProperties getLinkProperties(Network network) {
+ enforceAccessPermission();
+ NetworkAgentInfo nai = null;
+ synchronized (mNetworkForNetId) {
+ nai = mNetworkForNetId.get(network.netId);
+ }
+
+ if (nai != null) {
+ synchronized (nai) {
+ return new LinkProperties(nai.linkProperties);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public NetworkCapabilities getNetworkCapabilities(Network network) {
+ enforceAccessPermission();
+ NetworkAgentInfo nai = null;
+ synchronized (mNetworkForNetId) {
+ nai = mNetworkForNetId.get(network.netId);
+ }
+ if (nai != null) {
+ synchronized (nai) {
+ return new NetworkCapabilities(nai.networkCapabilities);
}
}
return null;
@@ -1100,11 +1287,13 @@
final int uid = Binder.getCallingUid();
final ArrayList<NetworkState> result = Lists.newArrayList();
synchronized (mRulesLock) {
- for (NetworkStateTracker tracker : mNetTrackers) {
- if (tracker != null) {
- final NetworkInfo info = getFilteredNetworkInfo(tracker, uid);
- result.add(new NetworkState(
- info, tracker.getLinkProperties(), tracker.getLinkCapabilities()));
+ for (int networkType = 0; networkType <= ConnectivityManager.MAX_NETWORK_TYPE;
+ networkType++) {
+ if (getNetworkInfoForType(networkType) != null) {
+ final NetworkInfo info = getFilteredNetworkInfo(networkType, uid);
+ final LinkProperties lp = getLinkPropertiesForTypeInternal(networkType);
+ final NetworkCapabilities netcap = getNetworkCapabilitiesForType(networkType);
+ result.add(new NetworkState(info, lp, netcap));
}
}
}
@@ -1113,10 +1302,11 @@
private NetworkState getNetworkStateUnchecked(int networkType) {
if (isNetworkTypeValid(networkType)) {
- final NetworkStateTracker tracker = mNetTrackers[networkType];
- if (tracker != null) {
- return new NetworkState(tracker.getNetworkInfo(), tracker.getLinkProperties(),
- tracker.getLinkCapabilities());
+ NetworkInfo info = getNetworkInfoForType(networkType);
+ if (info != null) {
+ return new NetworkState(info,
+ getLinkPropertiesForTypeInternal(networkType),
+ getNetworkCapabilitiesForType(networkType));
}
}
return null;
@@ -1163,29 +1353,11 @@
return false;
}
- public boolean setRadios(boolean turnOn) {
- boolean result = true;
- enforceChangePermission();
- for (NetworkStateTracker t : mNetTrackers) {
- if (t != null) 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);
- }
-
private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() {
@Override
- public void interfaceClassDataActivityChanged(String label, boolean active) {
+ public void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos) {
int deviceType = Integer.parseInt(label);
- sendDataActivityBroadcast(deviceType, active);
+ sendDataActivityBroadcast(deviceType, active, tsNanos);
}
};
@@ -1555,7 +1727,7 @@
continue;
}
- int prefix = destination.getNetworkPrefixLength();
+ int prefix = destination.getPrefixLength();
InetAddress addrMasked = NetworkUtils.getNetworkPart(address, prefix);
InetAddress destMasked = NetworkUtils.getNetworkPart(destination.getAddress(),
prefix);
@@ -1640,29 +1812,40 @@
if (DBG) log("requestRouteToHostAddress on invalid network: " + networkType);
return false;
}
- NetworkStateTracker tracker = mNetTrackers[networkType];
- DetailedState netState = DetailedState.DISCONNECTED;
- if (tracker != null) {
- netState = tracker.getNetworkInfo().getDetailedState();
- }
- if ((netState != DetailedState.CONNECTED &&
- netState != DetailedState.CAPTIVE_PORTAL_CHECK) ||
- tracker.isTeardownRequested()) {
- if (VDBG) {
- log("requestRouteToHostAddress on down network "
- + "(" + networkType + ") - dropped"
- + " tracker=" + tracker
- + " netState=" + netState
- + " isTeardownRequested="
- + ((tracker != null) ? tracker.isTeardownRequested() : "tracker:null"));
+ NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+ if (nai == null) {
+ if (mLegacyTypeTracker.isTypeSupported(networkType) == false) {
+ if (DBG) log("requestRouteToHostAddress on unsupported network: " + networkType);
+ } else {
+ if (DBG) log("requestRouteToHostAddress on down network: " + networkType);
}
return false;
}
+ DetailedState netState;
+ synchronized (nai) {
+ netState = nai.networkInfo.getDetailedState();
+ }
+
+ if ((netState != DetailedState.CONNECTED &&
+ netState != DetailedState.CAPTIVE_PORTAL_CHECK)) {
+ if (VDBG) {
+ log("requestRouteToHostAddress on down network "
+ + "(" + networkType + ") - dropped"
+ + " netState=" + netState);
+ }
+ return false;
+ }
+ final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- LinkProperties lp = tracker.getLinkProperties();
- boolean ok = addRouteToAddress(lp, addr, exempt);
+ LinkProperties lp;
+ int netId;
+ synchronized (nai) {
+ lp = nai.linkProperties;
+ netId = nai.network.netId;
+ }
+ boolean ok = modifyRouteToAddress(lp, addr, ADD, TO_DEFAULT_TABLE, exempt, netId, uid);
if (DBG) log("requestRouteToHostAddress ok=" + ok);
return ok;
} finally {
@@ -1671,24 +1854,16 @@
}
private boolean addRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable,
- boolean exempt) {
- return modifyRoute(p, r, 0, ADD, toDefaultTable, exempt);
+ boolean exempt, int netId) {
+ return modifyRoute(p, r, 0, ADD, toDefaultTable, exempt, netId, false, UID_UNUSED);
}
- private boolean removeRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable) {
- return modifyRoute(p, r, 0, REMOVE, toDefaultTable, UNEXEMPT);
- }
-
- private boolean addRouteToAddress(LinkProperties lp, InetAddress addr, boolean exempt) {
- return modifyRouteToAddress(lp, addr, ADD, TO_DEFAULT_TABLE, exempt);
- }
-
- private boolean removeRouteToAddress(LinkProperties lp, InetAddress addr) {
- return modifyRouteToAddress(lp, addr, REMOVE, TO_DEFAULT_TABLE, UNEXEMPT);
+ private boolean removeRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable, int netId) {
+ return modifyRoute(p, r, 0, REMOVE, toDefaultTable, UNEXEMPT, netId, false, UID_UNUSED);
}
private boolean modifyRouteToAddress(LinkProperties lp, InetAddress addr, boolean doAdd,
- boolean toDefaultTable, boolean exempt) {
+ boolean toDefaultTable, boolean exempt, int netId, int uid) {
RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getAllRoutes(), addr);
if (bestRoute == null) {
bestRoute = RouteInfo.makeHostRoute(addr, lp.getInterfaceName());
@@ -1703,11 +1878,18 @@
bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway(), iface);
}
}
- return modifyRoute(lp, bestRoute, 0, doAdd, toDefaultTable, exempt);
+ return modifyRoute(lp, bestRoute, 0, doAdd, toDefaultTable, exempt, netId, true, uid);
}
+ /*
+ * TODO: Clean all this stuff up. Once we have UID-based routing, stuff will break due to
+ * incorrect tracking of mAddedRoutes, so a cleanup becomes necessary and urgent. But at
+ * the same time, there'll be no more need to track mAddedRoutes or mExemptAddresses,
+ * or even have the concept of an exempt address, or do things like "selectBestRoute", or
+ * determine "default" vs "secondary" table, etc., so the cleanup becomes possible.
+ */
private boolean modifyRoute(LinkProperties lp, RouteInfo r, int cycleCount, boolean doAdd,
- boolean toDefaultTable, boolean exempt) {
+ boolean toDefaultTable, boolean exempt, int netId, boolean legacy, int uid) {
if ((lp == null) || (r == null)) {
if (DBG) log("modifyRoute got unexpected null: " + lp + ", " + r);
return false;
@@ -1736,7 +1918,8 @@
bestRoute.getGateway(),
ifaceName);
}
- modifyRoute(lp, bestRoute, cycleCount+1, doAdd, toDefaultTable, exempt);
+ modifyRoute(lp, bestRoute, cycleCount+1, doAdd, toDefaultTable, exempt, netId,
+ legacy, uid);
}
}
if (doAdd) {
@@ -1746,9 +1929,13 @@
synchronized (mRoutesLock) {
// only track default table - only one apps can effect
mAddedRoutes.add(r);
- mNetd.addRoute(ifaceName, r);
+ if (legacy) {
+ mNetd.addLegacyRouteForNetId(netId, r, uid);
+ } else {
+ mNetd.addRoute(netId, r);
+ }
if (exempt) {
- LinkAddress dest = r.getDestination();
+ LinkAddress dest = r.getDestinationLinkAddress();
if (!mExemptAddresses.contains(dest)) {
mNetd.setHostExemption(dest);
mExemptAddresses.add(dest);
@@ -1756,7 +1943,11 @@
}
}
} else {
- mNetd.addSecondaryRoute(ifaceName, r);
+ if (legacy) {
+ mNetd.addLegacyRouteForNetId(netId, r, uid);
+ } else {
+ mNetd.addRoute(netId, r);
+ }
}
} catch (Exception e) {
// never crash - catch them all
@@ -1772,8 +1963,12 @@
if (mAddedRoutes.contains(r) == false) {
if (VDBG) log("Removing " + r + " for interface " + ifaceName);
try {
- mNetd.removeRoute(ifaceName, r);
- LinkAddress dest = r.getDestination();
+ if (legacy) {
+ mNetd.removeLegacyRouteForNetId(netId, r, uid);
+ } else {
+ mNetd.removeRoute(netId, r);
+ }
+ LinkAddress dest = r.getDestinationLinkAddress();
if (mExemptAddresses.contains(dest)) {
mNetd.clearHostExemption(dest);
mExemptAddresses.remove(dest);
@@ -1790,7 +1985,11 @@
} else {
if (VDBG) log("Removing " + r + " for interface " + ifaceName);
try {
- mNetd.removeSecondaryRoute(ifaceName, r);
+ if (legacy) {
+ mNetd.removeLegacyRouteForNetId(netId, r, uid);
+ } else {
+ mNetd.removeRoute(netId, r);
+ }
} catch (Exception e) {
// never crash - catch them all
if (VDBG) loge("Exception trying to remove a route: " + e);
@@ -1801,20 +2000,6 @@
return true;
}
- /**
- * @see ConnectivityManager#getMobileDataEnabled()
- */
- public boolean getMobileDataEnabled() {
- // TODO: This detail should probably be in DataConnectionTracker's
- // which is where we store the value and maybe make this
- // asynchronous.
- enforceAccessPermission();
- boolean retVal = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.MOBILE_DATA, 1) == 1;
- if (VDBG) log("getMobileDataEnabled returning " + retVal);
- return retVal;
- }
-
public void setDataDependency(int networkType, boolean met) {
enforceConnectivityInternalPermission();
@@ -1887,32 +2072,6 @@
}
};
- /**
- * @see ConnectivityManager#setMobileDataEnabled(boolean)
- */
- public void setMobileDataEnabled(boolean enabled) {
- enforceChangePermission();
- if (DBG) log("setMobileDataEnabled(" + enabled + ")");
-
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA,
- (enabled ? ENABLED : DISABLED), 0));
- }
-
- private void handleSetMobileData(boolean enabled) {
- if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
- if (VDBG) {
- log(mNetTrackers[ConnectivityManager.TYPE_MOBILE].toString() + enabled);
- }
- mNetTrackers[ConnectivityManager.TYPE_MOBILE].setUserDataEnable(enabled);
- }
- if (mNetTrackers[ConnectivityManager.TYPE_WIMAX] != null) {
- if (VDBG) {
- log(mNetTrackers[ConnectivityManager.TYPE_WIMAX].toString() + enabled);
- }
- mNetTrackers[ConnectivityManager.TYPE_WIMAX].setUserDataEnable(enabled);
- }
- }
-
@Override
public void setPolicyDataEnable(int networkType, boolean enabled) {
// only someone like NPMS should only be calling us
@@ -1923,12 +2082,13 @@
}
private void handleSetPolicyDataEnable(int networkType, boolean enabled) {
- if (isNetworkTypeValid(networkType)) {
- final NetworkStateTracker tracker = mNetTrackers[networkType];
- if (tracker != null) {
- tracker.setPolicyDataEnable(enabled);
- }
- }
+ // TODO - handle this passing to factories
+// if (isNetworkTypeValid(networkType)) {
+// final NetworkStateTracker tracker = mNetTrackers[networkType];
+// if (tracker != null) {
+// tracker.setPolicyDataEnable(enabled);
+// }
+// }
}
private void enforceAccessPermission() {
@@ -1984,9 +2144,13 @@
int prevNetType = info.getType();
mNetTrackers[prevNetType].setTeardownRequested(false);
+ int thisNetId = mNetTrackers[prevNetType].getNetwork().netId;
// Remove idletimer previously setup in {@code handleConnect}
- removeDataActivityTracking(prevNetType);
+// Already in place in new function. This is dead code.
+// if (mNetConfigs[prevNetType].isDefault()) {
+// removeDataActivityTracking(prevNetType);
+// }
/*
* If the disconnected network is not the active one, then don't report
@@ -2053,7 +2217,8 @@
}
// do this before we broadcast the change
- handleConnectivityChange(prevNetType, doReset);
+// Already done in new function. This is dead code.
+// handleConnectivityChange(prevNetType, doReset);
final Intent immediateIntent = new Intent(intent);
immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
@@ -2067,6 +2232,13 @@
sendConnectedBroadcastDelayed(mNetTrackers[mActiveDefaultNetwork].getNetworkInfo(),
getConnectivityChangeDelay());
}
+ try {
+// mNetd.removeNetwork(thisNetId);
+ } catch (Exception e) {
+ loge("Exception removing network: " + e);
+ } finally {
+// mNetTrackers[prevNetType].setNetId(INVALID_NET_ID);
+ }
}
private void tryFailover(int prevNetType) {
@@ -2081,6 +2253,11 @@
log("tryFailover: set mActiveDefaultNetwork=-1, prevNetType=" + prevNetType);
}
mActiveDefaultNetwork = -1;
+ try {
+ mNetd.clearDefaultNetId();
+ } catch (Exception e) {
+ loge("Exception clearing default network :" + e);
+ }
}
// don't signal a reconnect for anything lower or equal priority than our
@@ -2167,10 +2344,11 @@
sendStickyBroadcastDelayed(makeGeneralIntent(info, bcastType), delayMs);
}
- private void sendDataActivityBroadcast(int deviceType, boolean active) {
+ private void sendDataActivityBroadcast(int deviceType, boolean active, long tsNanos) {
Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active);
+ intent.putExtra(ConnectivityManager.EXTRA_REALTIME_NS, tsNanos);
final long ident = Binder.clearCallingIdentity();
try {
mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
@@ -2180,67 +2358,6 @@
}
}
- /**
- * Called when an attempt to fail over to another network has failed.
- * @param info the {@link NetworkInfo} for the failed network
- */
- private void handleConnectionFailure(NetworkInfo info) {
- mNetTrackers[info.getType()].setTeardownRequested(false);
-
- String reason = info.getReason();
- String extraInfo = info.getExtraInfo();
-
- String reasonText;
- if (reason == null) {
- reasonText = ".";
- } else {
- reasonText = " (" + reason + ").";
- }
- loge("Attempt to connect to " + info.getTypeName() + " failed" + reasonText);
-
- Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, new NetworkInfo(info));
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
- if (getActiveNetworkInfo() == null) {
- intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
- }
- if (reason != null) {
- intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
- }
- if (extraInfo != null) {
- intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo);
- }
- if (info.isFailover()) {
- intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
- info.setFailover(false);
- }
-
- if (mNetConfigs[info.getType()].isDefault()) {
- tryFailover(info.getType());
- if (mActiveDefaultNetwork != -1) {
- NetworkInfo switchTo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
- intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
- } else {
- mDefaultInetConditionPublished = 0;
- intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
- }
- }
-
- intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished);
-
- final Intent immediateIntent = new Intent(intent);
- immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
- sendStickyBroadcast(immediateIntent);
- sendStickyBroadcast(intent);
- /*
- * If the failover network is already connected, then immediately send
- * out a followup broadcast indicating successful failover
- */
- if (mActiveDefaultNetwork != -1) {
- sendConnectedBroadcast(mNetTrackers[mActiveDefaultNetwork].getNetworkInfo());
- }
- }
-
private void sendStickyBroadcast(Intent intent) {
synchronized(this) {
if (!mSystemReady) {
@@ -2274,7 +2391,9 @@
}
void systemReady() {
- mCaptivePortalTracker = CaptivePortalTracker.makeCaptivePortalTracker(mContext, this);
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ mCaptivePortalTracker = CaptivePortalTracker.makeCaptivePortalTracker(mContext, this);
+ }
loadGlobalProxy();
synchronized(this) {
@@ -2318,8 +2437,6 @@
private void handleConnect(NetworkInfo info) {
final int newNetType = info.getType();
- setupDataActivityTracking(newNetType);
-
// snapshot isFailover, because sendConnectedBroadcast() resets it
boolean isFailover = info.isFailover();
final NetworkStateTracker thisNet = mNetTrackers[newNetType];
@@ -2335,17 +2452,23 @@
if (mNetConfigs[newNetType].isDefault()) {
if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != newNetType) {
if (isNewNetTypePreferredOverCurrentNetType(newNetType)) {
- // tear down the other
- NetworkStateTracker otherNet =
- mNetTrackers[mActiveDefaultNetwork];
- if (DBG) {
- log("Policy requires " + otherNet.getNetworkInfo().getTypeName() +
- " teardown");
- }
- if (!teardown(otherNet)) {
- loge("Network declined teardown request");
- teardown(thisNet);
- return;
+ String teardownPolicy = SystemProperties.get("net.teardownPolicy");
+ if (TextUtils.equals(teardownPolicy, "keep") == false) {
+ // tear down the other
+ NetworkStateTracker otherNet =
+ mNetTrackers[mActiveDefaultNetwork];
+ if (DBG) {
+ log("Policy requires " + otherNet.getNetworkInfo().getTypeName() +
+ " teardown");
+ }
+ if (!teardown(otherNet)) {
+ loge("Network declined teardown request");
+ teardown(thisNet);
+ return;
+ }
+ } else {
+ //TODO - remove
+ loge("network teardown skipped due to net.teardownPolicy setting");
}
} else {
// don't accept this one
@@ -2357,6 +2480,17 @@
return;
}
}
+ int thisNetId = nextNetId();
+ thisNet.setNetId(thisNetId);
+ try {
+// mNetd.createNetwork(thisNetId, thisIface);
+ } catch (Exception e) {
+ loge("Exception creating network :" + e);
+ teardown(thisNet);
+ return;
+ }
+// Already in place in new function. This is dead code.
+// setupDataActivityTracking(newNetType);
synchronized (ConnectivityService.this) {
// have a new default network, release the transition wakelock in a second
// if it's held. The second pause is to allow apps to reconnect over the
@@ -2369,6 +2503,11 @@
}
}
mActiveDefaultNetwork = newNetType;
+ try {
+ mNetd.setDefaultNetId(thisNetId);
+ } catch (Exception e) {
+ loge("Exception setting default network :" + e);
+ }
// this will cause us to come up initially as unconnected and switching
// to connected after our normal pause unless somebody reports us as reall
// disconnected
@@ -2378,10 +2517,21 @@
// Don't do this - if we never sign in stay, grey
//reportNetworkCondition(mActiveDefaultNetwork, 100);
updateNetworkSettings(thisNet);
+ } else {
+ int thisNetId = nextNetId();
+ thisNet.setNetId(thisNetId);
+ try {
+// mNetd.createNetwork(thisNetId, thisIface);
+ } catch (Exception e) {
+ loge("Exception creating network :" + e);
+ teardown(thisNet);
+ return;
+ }
}
thisNet.setTeardownRequested(false);
- updateMtuSizeSettings(thisNet);
- handleConnectivityChange(newNetType, false);
+// Already in place in new function. This is dead code.
+// updateMtuSizeSettings(thisNet);
+// handleConnectivityChange(newNetType, false);
sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
// notify battery stats service about this network
@@ -2394,75 +2544,49 @@
}
}
- private void handleCaptivePortalTrackerCheck(NetworkInfo info) {
- if (DBG) log("Captive portal check " + info);
- int type = info.getType();
- final NetworkStateTracker thisNet = mNetTrackers[type];
- if (mNetConfigs[type].isDefault()) {
- if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
- if (isNewNetTypePreferredOverCurrentNetType(type)) {
- if (DBG) log("Captive check on " + info.getTypeName());
- mCaptivePortalTracker.detectCaptivePortal(new NetworkInfo(info));
- return;
- } else {
- if (DBG) log("Tear down low priority net " + info.getTypeName());
- teardown(thisNet);
- return;
- }
- }
- }
-
- if (DBG) log("handleCaptivePortalTrackerCheck: call captivePortalCheckComplete ni=" + info);
- thisNet.captivePortalCheckComplete();
- }
-
- /** @hide */
- @Override
- public void captivePortalCheckComplete(NetworkInfo info) {
- enforceConnectivityInternalPermission();
- if (DBG) log("captivePortalCheckComplete: ni=" + info);
- mNetTrackers[info.getType()].captivePortalCheckComplete();
- }
-
/** @hide */
@Override
public void captivePortalCheckCompleted(NetworkInfo info, boolean isCaptivePortal) {
enforceConnectivityInternalPermission();
if (DBG) log("captivePortalCheckCompleted: ni=" + info + " captive=" + isCaptivePortal);
- mNetTrackers[info.getType()].captivePortalCheckCompleted(isCaptivePortal);
+// mNetTrackers[info.getType()].captivePortalCheckCompleted(isCaptivePortal);
}
/**
- * Setup data activity tracking for the given network interface.
+ * Setup data activity tracking for the given network.
*
* Every {@code setupDataActivityTracking} should be paired with a
- * {@link removeDataActivityTracking} for cleanup.
+ * {@link #removeDataActivityTracking} for cleanup.
*/
- private void setupDataActivityTracking(int type) {
- final NetworkStateTracker thisNet = mNetTrackers[type];
- final String iface = thisNet.getLinkProperties().getInterfaceName();
+ private void setupDataActivityTracking(NetworkAgentInfo networkAgent) {
+ final String iface = networkAgent.linkProperties.getInterfaceName();
final int timeout;
+ int type = ConnectivityManager.TYPE_NONE;
- if (ConnectivityManager.isNetworkTypeMobile(type)) {
+ if (networkAgent.networkCapabilities.hasTransport(
+ NetworkCapabilities.TRANSPORT_CELLULAR)) {
timeout = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
- 0);
- // Canonicalize mobile network type
+ 5);
type = ConnectivityManager.TYPE_MOBILE;
- } else if (ConnectivityManager.TYPE_WIFI == type) {
+ } else if (networkAgent.networkCapabilities.hasTransport(
+ NetworkCapabilities.TRANSPORT_WIFI)) {
timeout = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
0);
+ type = ConnectivityManager.TYPE_WIFI;
} else {
// do not track any other networks
timeout = 0;
}
- if (timeout > 0 && iface != null) {
+ if (timeout > 0 && iface != null && type != ConnectivityManager.TYPE_NONE) {
try {
- mNetd.addIdleTimer(iface, timeout, Integer.toString(type));
- } catch (RemoteException e) {
+ mNetd.addIdleTimer(iface, timeout, type);
+ } catch (Exception e) {
+ // You shall not crash!
+ loge("Exception in setupDataActivityTracking " + e);
}
}
}
@@ -2470,16 +2594,17 @@
/**
* Remove data activity tracking when network disconnects.
*/
- private void removeDataActivityTracking(int type) {
- final NetworkStateTracker net = mNetTrackers[type];
- final String iface = net.getLinkProperties().getInterfaceName();
+ private void removeDataActivityTracking(NetworkAgentInfo networkAgent) {
+ final String iface = networkAgent.linkProperties.getInterfaceName();
+ final NetworkCapabilities caps = networkAgent.networkCapabilities;
- if (iface != null && (ConnectivityManager.isNetworkTypeMobile(type) ||
- ConnectivityManager.TYPE_WIFI == type)) {
+ if (iface != null && (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
+ caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) {
try {
// the call fails silently if no idletimer setup for this interface
mNetd.removeIdleTimer(iface);
- } catch (RemoteException e) {
+ } catch (Exception e) {
+ loge("Exception in removeDataActivityTracking " + e);
}
}
}
@@ -2489,8 +2614,10 @@
* 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.
+ *
+ * TODO - delete when we're sure all this functionallity is captured.
*/
- private void handleConnectivityChange(int netType, boolean doReset) {
+ private void handleConnectivityChange(int netType, LinkProperties curLp, boolean doReset) {
int resetMask = doReset ? NetworkUtils.RESET_ALL_ADDRESSES : 0;
boolean exempt = ConnectivityManager.isNetworkTypeExempt(netType);
if (VDBG) {
@@ -2504,7 +2631,6 @@
*/
handleDnsConfigurationChange(netType);
- LinkProperties curLp = mCurrentLinkProperties[netType];
LinkProperties newLp = null;
if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
@@ -2534,9 +2660,9 @@
"\n car=" + car);
}
} else {
- if (DBG) {
- log("handleConnectivityChange: address are the same reset per doReset" +
- " linkProperty[" + netType + "]:" +
+ if (VDBG) {
+ log("handleConnectivityChange: addresses are the same reset per" +
+ " doReset linkProperty[" + netType + "]:" +
" resetMask=" + resetMask);
}
}
@@ -2561,7 +2687,8 @@
}
}
mCurrentLinkProperties[netType] = newLp;
- boolean resetDns = updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault(), exempt);
+ boolean resetDns = updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault(), exempt,
+ mNetTrackers[netType].getNetwork().netId);
if (resetMask != 0 || resetDns) {
if (VDBG) log("handleConnectivityChange: resetting");
@@ -2583,40 +2710,20 @@
}
}
}
- if (resetDns) {
- flushVmDnsCache();
- if (VDBG) log("resetting DNS cache for " + iface);
- try {
- mNetd.flushInterfaceDnsCache(iface);
- } catch (Exception e) {
- // never crash - catch them all
- if (DBG) loge("Exception resetting dns cache: " + e);
- }
- }
} else {
loge("Can't reset connection for type "+netType);
}
}
- }
- }
-
- // Update 464xlat state.
- NetworkStateTracker tracker = mNetTrackers[netType];
- if (mClat.requiresClat(netType, tracker)) {
-
- // If the connection was previously using clat, but is not using it now, stop the clat
- // daemon. Normally, this happens automatically when the connection disconnects, but if
- // the disconnect is not reported, or if the connection's LinkProperties changed for
- // some other reason (e.g., handoff changes the IP addresses on the link), it would
- // still be running. If it's not running, then stopping it is a no-op.
- if (Nat464Xlat.isRunningClat(curLp) && !Nat464Xlat.isRunningClat(newLp)) {
- mClat.stopClat();
- }
- // If the link requires clat to be running, then start the daemon now.
- if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
- mClat.startClat(tracker);
- } else {
- mClat.stopClat();
+ if (resetDns) {
+ flushVmDnsCache();
+ if (VDBG) log("resetting DNS cache for type " + netType);
+ try {
+ mNetd.flushNetworkDnsCache(mNetTrackers[netType].getNetwork().netId);
+ } catch (Exception e) {
+ // never crash - catch them all
+ if (DBG) loge("Exception resetting dns cache: " + e);
+ }
+ }
}
}
@@ -2640,7 +2747,7 @@
* returns a boolean indicating the routes changed
*/
private boolean updateRoutes(LinkProperties newLp, LinkProperties curLp,
- boolean isLinkDefault, boolean exempt) {
+ boolean isLinkDefault, boolean exempt, int netId) {
Collection<RouteInfo> routesToAdd = null;
CompareResult<InetAddress> dnsDiff = new CompareResult<InetAddress>();
CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>();
@@ -2650,7 +2757,7 @@
dnsDiff = curLp.compareDnses(newLp);
} else if (newLp != null) {
routeDiff.added = newLp.getAllRoutes();
- dnsDiff.added = newLp.getDnses();
+ dnsDiff.added = newLp.getDnsServers();
}
boolean routesChanged = (routeDiff.removed.size() != 0 || routeDiff.added.size() != 0);
@@ -2658,45 +2765,20 @@
for (RouteInfo r : routeDiff.removed) {
if (isLinkDefault || ! r.isDefaultRoute()) {
if (VDBG) log("updateRoutes: default remove route r=" + r);
- removeRoute(curLp, r, TO_DEFAULT_TABLE);
+ removeRoute(curLp, r, TO_DEFAULT_TABLE, netId);
}
if (isLinkDefault == false) {
// remove from a secondary route table
- removeRoute(curLp, r, TO_SECONDARY_TABLE);
- }
- }
-
- if (!isLinkDefault) {
- // handle DNS routes
- if (routesChanged) {
- // routes changed - remove all old dns entries and add new
- if (curLp != null) {
- for (InetAddress oldDns : curLp.getDnses()) {
- removeRouteToAddress(curLp, oldDns);
- }
- }
- if (newLp != null) {
- for (InetAddress newDns : newLp.getDnses()) {
- addRouteToAddress(newLp, newDns, exempt);
- }
- }
- } else {
- // no change in routes, check for change in dns themselves
- for (InetAddress oldDns : dnsDiff.removed) {
- removeRouteToAddress(curLp, oldDns);
- }
- for (InetAddress newDns : dnsDiff.added) {
- addRouteToAddress(newLp, newDns, exempt);
- }
+ removeRoute(curLp, r, TO_SECONDARY_TABLE, netId);
}
}
for (RouteInfo r : routeDiff.added) {
if (isLinkDefault || ! r.isDefaultRoute()) {
- addRoute(newLp, r, TO_DEFAULT_TABLE, exempt);
+ addRoute(newLp, r, TO_DEFAULT_TABLE, exempt, netId);
} else {
// add to a secondary route table
- addRoute(newLp, r, TO_SECONDARY_TABLE, UNEXEMPT);
+ addRoute(newLp, r, TO_SECONDARY_TABLE, UNEXEMPT, netId);
// many radios add a default route even when we don't want one.
// remove the default route unless somebody else has asked for it
@@ -2705,7 +2787,7 @@
if (!TextUtils.isEmpty(ifaceName) && !mAddedRoutes.contains(r)) {
if (VDBG) log("Removing " + r + " for interface " + ifaceName);
try {
- mNetd.removeRoute(ifaceName, r);
+ mNetd.removeRoute(netId, r);
} catch (Exception e) {
// never crash - catch them all
if (DBG) loge("Exception trying to remove a route: " + e);
@@ -2718,26 +2800,30 @@
return routesChanged;
}
- /**
+ /**
* Reads the network specific MTU size from reources.
* and set it on it's iface.
*/
- private void updateMtuSizeSettings(NetworkStateTracker nt) {
- final String iface = nt.getLinkProperties().getInterfaceName();
- final int mtu = nt.getLinkProperties().getMtu();
+ private void updateMtu(LinkProperties newLp, LinkProperties oldLp) {
+ final String iface = newLp.getInterfaceName();
+ final int mtu = newLp.getMtu();
+ if (oldLp != null && newLp.isIdenticalMtu(oldLp)) {
+ if (VDBG) log("identical MTU - not setting");
+ return;
+ }
- if (mtu < 68 || mtu > 10000) {
- loge("Unexpected mtu value: " + nt);
- return;
- }
+ if (mtu < 68 || mtu > 10000) {
+ loge("Unexpected mtu value: " + mtu + ", " + iface);
+ return;
+ }
- try {
- if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
- mNetd.setMtu(iface, mtu);
- } catch (Exception e) {
- Slog.e(TAG, "exception in setMtu()" + e);
- }
- }
+ try {
+ if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
+ mNetd.setMtu(iface, mtu);
+ } catch (Exception e) {
+ Slog.e(TAG, "exception in setMtu()" + e);
+ }
+ }
/**
* Reads the network specific TCP buffer sizes from SystemProperties
@@ -2822,7 +2908,8 @@
if (p == null) continue;
if (mNetRequestersPids[i].contains(myPid)) {
try {
- mNetd.setDnsInterfaceForPid(p.getInterfaceName(), pid);
+ // TODO: Reimplement this via local variable in bionic.
+ // mNetd.setDnsNetworkForPid(nt.getNetwork().netId, pid);
} catch (Exception e) {
Slog.e(TAG, "exception reasseses pid dns: " + e);
}
@@ -2832,7 +2919,8 @@
}
// nothing found - delete
try {
- mNetd.clearDnsInterfaceForPid(pid);
+ // TODO: Reimplement this via local variable in bionic.
+ // mNetd.clearDnsNetworkForPid(pid);
} catch (Exception e) {
Slog.e(TAG, "exception clear interface from pid: " + e);
}
@@ -2857,8 +2945,8 @@
}
// Caller must grab mDnsLock.
- private void updateDnsLocked(String network, String iface,
- Collection<InetAddress> dnses, String domains, boolean defaultDns) {
+ private void updateDnsLocked(String network, int netId,
+ Collection<InetAddress> dnses, String domains) {
int last = 0;
if (dnses.size() == 0 && mDefaultDns != null) {
dnses = new ArrayList();
@@ -2869,10 +2957,7 @@
}
try {
- mNetd.setDnsServersForInterface(iface, NetworkUtils.makeStrings(dnses), domains);
- if (defaultDns) {
- mNetd.setDefaultInterfaceForDns(iface);
- }
+ mNetd.setDnsServersForNetwork(netId, NetworkUtils.makeStrings(dnses), domains);
for (InetAddress dns : dnses) {
++last;
@@ -2896,15 +2981,16 @@
if (nt != null && nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
LinkProperties p = nt.getLinkProperties();
if (p == null) return;
- Collection<InetAddress> dnses = p.getDnses();
+ Collection<InetAddress> dnses = p.getDnsServers();
+ int netId = nt.getNetwork().netId;
if (mNetConfigs[netType].isDefault()) {
String network = nt.getNetworkInfo().getTypeName();
synchronized (mDnsLock) {
- updateDnsLocked(network, p.getInterfaceName(), dnses, p.getDomains(), true);
+ updateDnsLocked(network, netId, dnses, p.getDomains());
}
} else {
try {
- mNetd.setDnsServersForInterface(p.getInterfaceName(),
+ mNetd.setDnsServersForNetwork(netId,
NetworkUtils.makeStrings(dnses), p.getDomains());
} catch (Exception e) {
if (DBG) loge("exception setting dns servers: " + e);
@@ -2913,7 +2999,8 @@
List<Integer> pids = mNetRequestersPids[netType];
for (Integer pid : pids) {
try {
- mNetd.setDnsInterfaceForPid(p.getInterfaceName(), pid);
+ // TODO: Reimplement this via local variable in bionic.
+ // mNetd.setDnsNetworkForPid(netId, pid);
} catch (Exception e) {
Slog.e(TAG, "exception setting interface for pid: " + e);
}
@@ -2923,7 +3010,8 @@
}
}
- private int getRestoreDefaultNetworkDelay(int networkType) {
+ @Override
+ public int getRestoreDefaultNetworkDelay(int networkType) {
String restoreDefaultNetworkDelayStr = SystemProperties.get(
NETWORK_RESTORE_DELAY_PROP_NAME);
if(restoreDefaultNetworkDelayStr != null &&
@@ -2955,41 +3043,47 @@
return;
}
- // TODO: add locking to get atomic snapshot
- pw.println();
- for (int i = 0; i < mNetTrackers.length; i++) {
- final NetworkStateTracker nst = mNetTrackers[i];
- if (nst != null) {
- pw.println("NetworkStateTracker for " + getNetworkTypeName(i) + ":");
- pw.increaseIndent();
- if (nst.getNetworkInfo().isConnected()) {
- pw.println("Active network: " + nst.getNetworkInfo().
- getTypeName());
- }
- pw.println(nst.getNetworkInfo());
- pw.println(nst.getLinkProperties());
- pw.println(nst);
- pw.println();
- pw.decreaseIndent();
- }
- }
-
- pw.println("Network Requester Pids:");
+ pw.println("NetworkFactories for:");
pw.increaseIndent();
- for (int net : mPriorityList) {
- String pidString = net + ": ";
- for (Integer pid : mNetRequestersPids[net]) {
- pidString = pidString + pid.toString() + ", ";
- }
- pw.println(pidString);
+ for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
+ pw.println(nfi.name);
}
- pw.println();
pw.decreaseIndent();
+ pw.println();
- pw.println("FeatureUsers:");
+ NetworkAgentInfo defaultNai = mNetworkForRequestId.get(mDefaultRequest.requestId);
+ pw.print("Active default network: ");
+ if (defaultNai == null) {
+ pw.println("none");
+ } else {
+ pw.println(defaultNai.network.netId);
+ }
+ pw.println();
+
+ pw.println("Current Networks:");
pw.increaseIndent();
- for (Object requester : mFeatureUsers) {
- pw.println(requester.toString());
+ for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ pw.println(nai.toString());
+ pw.increaseIndent();
+ pw.println("Requests:");
+ pw.increaseIndent();
+ for (int i = 0; i < nai.networkRequests.size(); i++) {
+ pw.println(nai.networkRequests.valueAt(i).toString());
+ }
+ pw.decreaseIndent();
+ pw.println("Lingered:");
+ pw.increaseIndent();
+ for (NetworkRequest nr : nai.networkLingered) pw.println(nr.toString());
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ pw.println();
+
+ pw.println("Network Requests:");
+ pw.increaseIndent();
+ for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+ pw.println(nri.toString());
}
pw.println();
pw.decreaseIndent();
@@ -3024,6 +3118,106 @@
public void handleMessage(Message msg) {
NetworkInfo info;
switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
+ handleAsyncChannelHalfConnect(msg);
+ break;
+ }
+ case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai != null) nai.asyncChannel.disconnect();
+ break;
+ }
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+ handleAsyncChannelDisconnected(msg);
+ break;
+ }
+ case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_NETWORK_CAPABILITIES_CHANGED from unknown NetworkAgent");
+ } else {
+ updateCapabilities(nai, (NetworkCapabilities)msg.obj);
+ }
+ break;
+ }
+ case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("NetworkAgent not found for EVENT_NETWORK_PROPERTIES_CHANGED");
+ } else {
+ if (VDBG) log("Update of Linkproperties for " + nai.name());
+ LinkProperties oldLp = nai.linkProperties;
+ synchronized (nai) {
+ nai.linkProperties = (LinkProperties)msg.obj;
+ }
+ updateLinkProperties(nai, oldLp);
+ }
+ break;
+ }
+ case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_NETWORK_INFO_CHANGED from unknown NetworkAgent");
+ break;
+ }
+ info = (NetworkInfo) msg.obj;
+ updateNetworkInfo(nai, info);
+ break;
+ }
+ case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_NETWORK_SCORE_CHANGED from unknown NetworkAgent");
+ break;
+ }
+ Integer score = (Integer) msg.obj;
+ if (score != null) updateNetworkScore(nai, score.intValue());
+ break;
+ }
+ case NetworkAgent.EVENT_UID_RANGES_ADDED: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_UID_RANGES_ADDED from unknown NetworkAgent");
+ break;
+ }
+ try {
+ mNetd.addVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
+ } catch (RemoteException e) {
+ }
+ break;
+ }
+ case NetworkAgent.EVENT_UID_RANGES_REMOVED: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_UID_RANGES_REMOVED from unknown NetworkAgent");
+ break;
+ }
+ try {
+ mNetd.removeVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
+ } catch (RemoteException e) {
+ }
+ break;
+ }
+ case NetworkMonitor.EVENT_NETWORK_VALIDATED: {
+ NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
+ handleConnectionValidated(nai);
+ break;
+ }
+ case NetworkMonitor.EVENT_NETWORK_LINGER_COMPLETE: {
+ NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
+ handleLingerComplete(nai);
+ break;
+ }
+ case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
+ break;
+ }
+ setProvNotificationVisibleIntent(msg.arg1 != 0, nai.networkInfo.getType(),
+ nai.networkInfo.getExtraInfo(), (PendingIntent)msg.obj);
+ break;
+ }
case NetworkStateTracker.EVENT_STATE_CHANGED: {
info = (NetworkInfo) msg.obj;
NetworkInfo.State state = info.getState();
@@ -3056,13 +3250,7 @@
EventLogTags.writeConnectivityStateChanged(
info.getType(), info.getSubtype(), info.getDetailedState().ordinal());
- if (info.getDetailedState() ==
- NetworkInfo.DetailedState.FAILED) {
- handleConnectionFailure(info);
- } else if (info.getDetailedState() ==
- DetailedState.CAPTIVE_PORTAL_CHECK) {
- handleCaptivePortalTrackerCheck(info);
- } else if (info.isConnectedToProvisioningNetwork()) {
+ if (info.isConnectedToProvisioningNetwork()) {
/**
* TODO: Create ConnectivityManager.TYPE_MOBILE_PROVISIONING
* for now its an in between network, its a network that
@@ -3073,7 +3261,7 @@
* to the link that may have incorrectly setup by the lower
* levels.
*/
- LinkProperties lp = getLinkProperties(info.getType());
+ LinkProperties lp = getLinkPropertiesForTypeInternal(info.getType());
if (DBG) {
log("EVENT_STATE_CHANGED: connected to provisioning network, lp=" + lp);
}
@@ -3083,21 +3271,13 @@
// connection will fail until the provisioning network
// is enabled.
for (RouteInfo r : lp.getRoutes()) {
- removeRoute(lp, r, TO_DEFAULT_TABLE);
+ removeRoute(lp, r, TO_DEFAULT_TABLE,
+ mNetTrackers[info.getType()].getNetwork().netId);
}
} else if (state == NetworkInfo.State.DISCONNECTED) {
- handleDisconnect(info);
} else if (state == 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 (state == NetworkInfo.State.CONNECTED) {
- handleConnect(info);
+ // handleConnect(info);
}
if (mLockdownTracker != null) {
mLockdownTracker.onNetworkInfoChanged(info);
@@ -3109,7 +3289,8 @@
// TODO: Temporary allowing network configuration
// change not resetting sockets.
// @see bug/4455071
- handleConnectivityChange(info.getType(), false);
+ handleConnectivityChange(info.getType(), mCurrentLinkProperties[info.getType()],
+ false);
break;
}
case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: {
@@ -3122,6 +3303,200 @@
}
}
+ private void handleAsyncChannelHalfConnect(Message msg) {
+ AsyncChannel ac = (AsyncChannel) msg.obj;
+ if (mNetworkFactoryInfos.containsKey(msg.replyTo)) {
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ if (VDBG) log("NetworkFactory connected");
+ // A network factory has connected. Send it all current NetworkRequests.
+ for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.isRequest == false) continue;
+ NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
+ ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK,
+ (nai != null ? nai.currentScore : 0), 0, nri.request);
+ }
+ } else {
+ loge("Error connecting NetworkFactory");
+ mNetworkFactoryInfos.remove(msg.obj);
+ }
+ } else if (mNetworkAgentInfos.containsKey(msg.replyTo)) {
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ if (VDBG) log("NetworkAgent connected");
+ // A network agent has requested a connection. Establish the connection.
+ mNetworkAgentInfos.get(msg.replyTo).asyncChannel.
+ sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ } else {
+ loge("Error connecting NetworkAgent");
+ NetworkAgentInfo nai = mNetworkAgentInfos.remove(msg.replyTo);
+ if (nai != null) {
+ synchronized (mNetworkForNetId) {
+ mNetworkForNetId.remove(nai.network.netId);
+ }
+ mLegacyTypeTracker.remove(nai);
+ }
+ }
+ }
+ }
+ private void handleAsyncChannelDisconnected(Message msg) {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai != null) {
+ if (DBG) {
+ log(nai.name() + " got DISCONNECTED, was satisfying " + nai.networkRequests.size());
+ }
+ // A network agent has disconnected.
+ // Tell netd to clean up the configuration for this network
+ // (routing rules, DNS, etc).
+ try {
+ mNetd.removeNetwork(nai.network.netId);
+ } catch (Exception e) {
+ loge("Exception removing network: " + e);
+ }
+ // TODO - if we move the logic to the network agent (have them disconnect
+ // because they lost all their requests or because their score isn't good)
+ // then they would disconnect organically, report their new state and then
+ // disconnect the channel.
+ if (nai.networkInfo.isConnected()) {
+ nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
+ null, null);
+ }
+ notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
+ nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
+ mNetworkAgentInfos.remove(msg.replyTo);
+ updateClat(null, nai.linkProperties, nai);
+ mLegacyTypeTracker.remove(nai);
+ synchronized (mNetworkForNetId) {
+ mNetworkForNetId.remove(nai.network.netId);
+ }
+ // Since we've lost the network, go through all the requests that
+ // it was satisfying and see if any other factory can satisfy them.
+ final ArrayList<NetworkAgentInfo> toActivate = new ArrayList<NetworkAgentInfo>();
+ for (int i = 0; i < nai.networkRequests.size(); i++) {
+ NetworkRequest request = nai.networkRequests.valueAt(i);
+ NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId);
+ if (VDBG) {
+ log(" checking request " + request + ", currentNetwork = " +
+ (currentNetwork != null ? currentNetwork.name() : "null"));
+ }
+ if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
+ mNetworkForRequestId.remove(request.requestId);
+ sendUpdatedScoreToFactories(request, 0);
+ NetworkAgentInfo alternative = null;
+ for (Map.Entry entry : mNetworkAgentInfos.entrySet()) {
+ NetworkAgentInfo existing = (NetworkAgentInfo)entry.getValue();
+ if (existing.networkInfo.isConnected() &&
+ request.networkCapabilities.satisfiedByNetworkCapabilities(
+ existing.networkCapabilities) &&
+ (alternative == null ||
+ alternative.currentScore < existing.currentScore)) {
+ alternative = existing;
+ }
+ }
+ if (alternative != null && !toActivate.contains(alternative)) {
+ toActivate.add(alternative);
+ }
+ }
+ }
+ if (nai.networkRequests.get(mDefaultRequest.requestId) != null) {
+ removeDataActivityTracking(nai);
+ mActiveDefaultNetwork = ConnectivityManager.TYPE_NONE;
+ requestNetworkTransitionWakelock(nai.name());
+ }
+ for (NetworkAgentInfo networkToActivate : toActivate) {
+ networkToActivate.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
+ }
+ }
+ }
+
+ private void handleRegisterNetworkRequest(Message msg) {
+ final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
+ final NetworkCapabilities newCap = nri.request.networkCapabilities;
+ int score = 0;
+
+ // Check for the best currently alive network that satisfies this request
+ NetworkAgentInfo bestNetwork = null;
+ for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
+ if (VDBG) log("handleRegisterNetworkRequest checking " + network.name());
+ if (newCap.satisfiedByNetworkCapabilities(network.networkCapabilities)) {
+ if (VDBG) log("apparently satisfied. currentScore=" + network.currentScore);
+ if ((bestNetwork == null) || bestNetwork.currentScore < network.currentScore) {
+ bestNetwork = network;
+ }
+ }
+ }
+ if (bestNetwork != null) {
+ if (VDBG) log("using " + bestNetwork.name());
+ if (nri.isRequest && bestNetwork.networkInfo.isConnected()) {
+ // Cancel any lingering so the linger timeout doesn't teardown this network
+ // even though we have a request for it.
+ bestNetwork.networkLingered.clear();
+ bestNetwork.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
+ }
+ bestNetwork.addRequest(nri.request);
+ mNetworkForRequestId.put(nri.request.requestId, bestNetwork);
+ int legacyType = nri.request.legacyType;
+ if (legacyType != TYPE_NONE) {
+ mLegacyTypeTracker.add(legacyType, bestNetwork);
+ }
+ notifyNetworkCallback(bestNetwork, nri);
+ score = bestNetwork.currentScore;
+ }
+ mNetworkRequests.put(nri.request, nri);
+ if (msg.what == EVENT_REGISTER_NETWORK_REQUEST) {
+ if (DBG) log("sending new NetworkRequest to factories");
+ for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
+ nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
+ 0, nri.request);
+ }
+ }
+ }
+
+ private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) {
+ NetworkRequestInfo nri = mNetworkRequests.get(request);
+ if (nri != null) {
+ if (nri.mUid != callingUid) {
+ if (DBG) log("Attempt to release unowned NetworkRequest " + request);
+ return;
+ }
+ if (DBG) log("releasing NetworkRequest " + request);
+ mNetworkRequests.remove(request);
+ // tell the network currently servicing this that it's no longer interested
+ NetworkAgentInfo affectedNetwork = mNetworkForRequestId.get(nri.request.requestId);
+ if (affectedNetwork != null) {
+ mNetworkForRequestId.remove(nri.request.requestId);
+ affectedNetwork.networkRequests.remove(nri.request.requestId);
+ if (VDBG) {
+ log(" Removing from current network " + affectedNetwork.name() + ", leaving " +
+ affectedNetwork.networkRequests.size() + " requests.");
+ }
+ }
+
+ if (nri.isRequest) {
+ for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
+ nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST,
+ nri.request);
+ }
+
+ if (affectedNetwork != null) {
+ // check if this network still has live requests - otherwise, tear down
+ // TODO - probably push this to the NF/NA
+ boolean keep = affectedNetwork.isVPN();
+ for (int i = 0; i < affectedNetwork.networkRequests.size() && !keep; i++) {
+ NetworkRequest r = affectedNetwork.networkRequests.valueAt(i);
+ if (mNetworkRequests.get(r).isRequest) {
+ keep = true;
+ }
+ }
+ if (keep == false) {
+ if (DBG) log("no live requests for " + affectedNetwork.name() +
+ "; disconnecting");
+ affectedNetwork.asyncChannel.disconnect();
+ }
+ }
+ }
+ callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_RELEASED);
+ }
+ }
+
private class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
@@ -3131,6 +3506,7 @@
public void handleMessage(Message msg) {
NetworkInfo info;
switch (msg.what) {
+ case EVENT_EXPIRE_NET_TRANSITION_WAKELOCK:
case EVENT_CLEAR_NET_TRANSITION_WAKELOCK: {
String causedBy = null;
synchronized (ConnectivityService.this) {
@@ -3138,10 +3514,15 @@
mNetTransitionWakeLock.isHeld()) {
mNetTransitionWakeLock.release();
causedBy = mNetTransitionWakeLockCausedBy;
+ } else {
+ break;
}
}
- if (causedBy != null) {
- log("NetTransition Wakelock for " + causedBy + " released by timeout");
+ if (msg.what == EVENT_EXPIRE_NET_TRANSITION_WAKELOCK) {
+ log("Failed to find a new network - expiring NetTransition Wakelock");
+ } else {
+ log("NetTransition Wakelock (" + (causedBy == null ? "unknown" : causedBy) +
+ " cleared because we found a replacement network");
}
break;
}
@@ -3162,16 +3543,6 @@
handleInetConditionHoldEnd(netType, sequence);
break;
}
- case EVENT_SET_NETWORK_PREFERENCE: {
- int preference = msg.arg1;
- handleSetNetworkPreference(preference);
- break;
- }
- case EVENT_SET_MOBILE_DATA: {
- boolean enabled = (msg.arg1 == ENABLED);
- handleSetMobileData(enabled);
- break;
- }
case EVENT_APPLY_GLOBAL_HTTP_PROXY: {
handleDeprecatedGlobalHttpProxy();
break;
@@ -3192,12 +3563,6 @@
handleSetPolicyDataEnable(networkType, enabled);
break;
}
- case EVENT_VPN_STATE_CHANGED: {
- if (mLockdownTracker != null) {
- mLockdownTracker.onVpnStateChanged((NetworkInfo) msg.obj);
- }
- break;
- }
case EVENT_ENABLE_FAIL_FAST_MOBILE_DATA: {
int tag = mEnableFailFastMobileDataTag.get();
if (msg.arg1 == tag) {
@@ -3217,7 +3582,28 @@
break;
}
case EVENT_PROXY_HAS_CHANGED: {
- handleApplyDefaultProxy((ProxyProperties)msg.obj);
+ handleApplyDefaultProxy((ProxyInfo)msg.obj);
+ break;
+ }
+ case EVENT_REGISTER_NETWORK_FACTORY: {
+ handleRegisterNetworkFactory((NetworkFactoryInfo)msg.obj);
+ break;
+ }
+ case EVENT_UNREGISTER_NETWORK_FACTORY: {
+ handleUnregisterNetworkFactory((Messenger)msg.obj);
+ break;
+ }
+ case EVENT_REGISTER_NETWORK_AGENT: {
+ handleRegisterNetworkAgent((NetworkAgentInfo)msg.obj);
+ break;
+ }
+ case EVENT_REGISTER_NETWORK_REQUEST:
+ case EVENT_REGISTER_NETWORK_LISTENER: {
+ handleRegisterNetworkRequest(msg);
+ break;
+ }
+ case EVENT_RELEASE_NETWORK_REQUEST: {
+ handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.arg1);
break;
}
}
@@ -3311,6 +3697,11 @@
return mTethering.getErroredIfaces();
}
+ public String[] getTetheredDhcpRanges() {
+ enforceConnectivityInternalPermission();
+ return mTethering.getTetheredDhcpRanges();
+ }
+
// if ro.tether.denied = true we default to no tethering
// gservices could set the secure setting to 1 though to enable it on a build where it
// had previously been turned off.
@@ -3318,28 +3709,28 @@
enforceTetherAccessPermission();
int defaultVal = (SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1);
boolean tetherEnabledInSettings = (Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.TETHER_SUPPORTED, defaultVal) != 0);
+ Settings.Global.TETHER_SUPPORTED, defaultVal) != 0)
+ && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
return tetherEnabledInSettings && ((mTethering.getTetherableUsbRegexs().length != 0 ||
mTethering.getTetherableWifiRegexs().length != 0 ||
mTethering.getTetherableBluetoothRegexs().length != 0) &&
mTethering.getUpstreamIfaceTypes().length != 0);
}
- // An API NetworkStateTrackers can call when they lose their network.
- // This will automatically be cleared after X seconds or a network becomes CONNECTED,
- // whichever happens first. The timer is started by the first caller and not
- // restarted by subsequent callers.
- public void requestNetworkTransitionWakelock(String forWhom) {
- enforceConnectivityInternalPermission();
+ // Called when we lose the default network and have no replacement yet.
+ // This will automatically be cleared after X seconds or a new default network
+ // becomes CONNECTED, whichever happens first. The timer is started by the
+ // first caller and not restarted by subsequent callers.
+ private void requestNetworkTransitionWakelock(String forWhom) {
+ int serialNum = 0;
synchronized (this) {
if (mNetTransitionWakeLock.isHeld()) return;
- mNetTransitionWakeLockSerialNumber++;
+ serialNum = ++mNetTransitionWakeLockSerialNumber;
mNetTransitionWakeLock.acquire();
mNetTransitionWakeLockCausedBy = forWhom;
}
mHandler.sendMessageDelayed(mHandler.obtainMessage(
- EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
- mNetTransitionWakeLockSerialNumber, 0),
+ EVENT_EXPIRE_NET_TRANSITION_WAKELOCK, serialNum, 0),
mNetTransitionWakeLockTimeout);
return;
}
@@ -3366,6 +3757,10 @@
EVENT_INET_CONDITION_CHANGE, networkType, percentage));
}
+ public void reportBadNetwork(Network network) {
+ //TODO
+ }
+
private void handleInetConditionChange(int netType, int condition) {
if (mActiveDefaultNetwork == -1) {
if (DBG) log("handleInetConditionChange: no active default network - ignore");
@@ -3425,7 +3820,7 @@
// if (DBG) log("no change in condition - aborting");
// return;
//}
- NetworkInfo networkInfo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
+ NetworkInfo networkInfo = getNetworkInfoForType(mActiveDefaultNetwork);
if (networkInfo.isConnected() == false) {
if (DBG) log("handleInetConditionHoldEnd: default network not connected - ignoring");
return;
@@ -3435,19 +3830,19 @@
return;
}
- public ProxyProperties getProxy() {
+ public ProxyInfo getProxy() {
// this information is already available as a world read/writable jvm property
// so this API change wouldn't have a benifit. It also breaks the passing
// of proxy info to all the JVMs.
// enforceAccessPermission();
synchronized (mProxyLock) {
- ProxyProperties ret = mGlobalProxy;
+ ProxyInfo ret = mGlobalProxy;
if ((ret == null) && !mDefaultProxyDisabled) ret = mDefaultProxy;
return ret;
}
}
- public void setGlobalProxy(ProxyProperties proxyProperties) {
+ public void setGlobalProxy(ProxyInfo proxyProperties) {
enforceConnectivityInternalPermission();
synchronized (mProxyLock) {
@@ -3460,18 +3855,18 @@
String exclList = "";
String pacFileUrl = "";
if (proxyProperties != null && (!TextUtils.isEmpty(proxyProperties.getHost()) ||
- !TextUtils.isEmpty(proxyProperties.getPacFileUrl()))) {
+ (proxyProperties.getPacFileUrl() != null))) {
if (!proxyProperties.isValid()) {
if (DBG)
log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
return;
}
- mGlobalProxy = new ProxyProperties(proxyProperties);
+ mGlobalProxy = new ProxyInfo(proxyProperties);
host = mGlobalProxy.getHost();
port = mGlobalProxy.getPort();
- exclList = mGlobalProxy.getExclusionList();
+ exclList = mGlobalProxy.getExclusionListAsString();
if (proxyProperties.getPacFileUrl() != null) {
- pacFileUrl = proxyProperties.getPacFileUrl();
+ pacFileUrl = proxyProperties.getPacFileUrl().toString();
}
} else {
mGlobalProxy = null;
@@ -3503,11 +3898,11 @@
Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC);
if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
- ProxyProperties proxyProperties;
+ ProxyInfo proxyProperties;
if (!TextUtils.isEmpty(pacFileUrl)) {
- proxyProperties = new ProxyProperties(pacFileUrl);
+ proxyProperties = new ProxyInfo(pacFileUrl);
} else {
- proxyProperties = new ProxyProperties(host, port, exclList);
+ proxyProperties = new ProxyInfo(host, port, exclList);
}
if (!proxyProperties.isValid()) {
if (DBG) log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
@@ -3520,7 +3915,7 @@
}
}
- public ProxyProperties getGlobalProxy() {
+ public ProxyInfo getGlobalProxy() {
// this information is already available as a world read/writable jvm property
// so this API change wouldn't have a benifit. It also breaks the passing
// of proxy info to all the JVMs.
@@ -3530,9 +3925,9 @@
}
}
- private void handleApplyDefaultProxy(ProxyProperties proxy) {
+ private void handleApplyDefaultProxy(ProxyInfo proxy) {
if (proxy != null && TextUtils.isEmpty(proxy.getHost())
- && TextUtils.isEmpty(proxy.getPacFileUrl())) {
+ && (proxy.getPacFileUrl() == null)) {
proxy = null;
}
synchronized (mProxyLock) {
@@ -3542,6 +3937,18 @@
if (DBG) log("Invalid proxy properties, ignoring: " + proxy.toString());
return;
}
+
+ // This call could be coming from the PacManager, containing the port of the local
+ // proxy. If this new proxy matches the global proxy then copy this proxy to the
+ // global (to get the correct local port), and send a broadcast.
+ // TODO: Switch PacManager to have its own message to send back rather than
+ // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
+ if ((mGlobalProxy != null) && (proxy != null) && (proxy.getPacFileUrl() != null)
+ && proxy.getPacFileUrl().equals(mGlobalProxy.getPacFileUrl())) {
+ mGlobalProxy = proxy;
+ sendProxyBroadcast(mGlobalProxy);
+ return;
+ }
mDefaultProxy = proxy;
if (mGlobalProxy != null) return;
@@ -3569,13 +3976,13 @@
return;
}
}
- ProxyProperties p = new ProxyProperties(data[0], proxyPort, "");
+ ProxyInfo p = new ProxyInfo(data[0], proxyPort, "");
setGlobalProxy(p);
}
}
- private void sendProxyBroadcast(ProxyProperties proxy) {
- if (proxy == null) proxy = new ProxyProperties("", 0, "");
+ private void sendProxyBroadcast(ProxyInfo proxy) {
+ if (proxy == null) proxy = new ProxyInfo("", 0, "");
if (mPacManager.setCurrentProxyScriptUrl(proxy)) return;
if (DBG) log("sending Proxy Broadcast for " + proxy);
Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
@@ -3638,6 +4045,8 @@
usedNetworkType = ConnectivityManager.TYPE_MOBILE_IMS;
} else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_CBS)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_CBS;
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_EMERGENCY)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_EMERGENCY;
} else {
Slog.e(TAG, "Can't match any mobile netTracker!");
}
@@ -3661,36 +4070,6 @@
}
/**
- * Protect a socket from VPN routing rules. This method is used by
- * VpnBuilder and not available in ConnectivityManager. Permissions
- * are checked in Vpn class.
- * @hide
- */
- @Override
- public boolean protectVpn(ParcelFileDescriptor socket) {
- throwIfLockdownEnabled();
- try {
- int type = mActiveDefaultNetwork;
- int user = UserHandle.getUserId(Binder.getCallingUid());
- if (ConnectivityManager.isNetworkTypeValid(type) && mNetTrackers[type] != null) {
- synchronized(mVpns) {
- mVpns.get(user).protect(socket);
- }
- return true;
- }
- } catch (Exception e) {
- // ignore
- } finally {
- try {
- socket.close();
- } catch (Exception e) {
- // ignore
- }
- }
- return false;
- }
-
- /**
* Prepare for a VPN application. This method is used by VpnDialogs
* and not available in ConnectivityManager. Permissions are checked
* in Vpn class.
@@ -3784,143 +4163,6 @@
}
}
- /**
- * Callback for VPN subsystem. Currently VPN is not adapted to the service
- * through NetworkStateTracker since it works differently. For example, it
- * needs to override DNS servers but never takes the default routes. It
- * relies on another data network, and it could keep existing connections
- * alive after reconnecting, switching between networks, or even resuming
- * from deep sleep. Calls from applications should be done synchronously
- * to avoid race conditions. As these are all hidden APIs, refactoring can
- * be done whenever a better abstraction is developed.
- */
- public class VpnCallback {
- private VpnCallback() {
- }
-
- public void onStateChanged(NetworkInfo info) {
- mHandler.obtainMessage(EVENT_VPN_STATE_CHANGED, info).sendToTarget();
- }
-
- public void override(String iface, List<String> dnsServers, List<String> searchDomains) {
- if (dnsServers == null) {
- restore();
- return;
- }
-
- // Convert DNS servers into addresses.
- List<InetAddress> addresses = new ArrayList<InetAddress>();
- for (String address : dnsServers) {
- // Double check the addresses and remove invalid ones.
- try {
- addresses.add(InetAddress.parseNumericAddress(address));
- } catch (Exception e) {
- // ignore
- }
- }
- if (addresses.isEmpty()) {
- restore();
- return;
- }
-
- // Concatenate search domains into a string.
- StringBuilder buffer = new StringBuilder();
- if (searchDomains != null) {
- for (String domain : searchDomains) {
- buffer.append(domain).append(' ');
- }
- }
- String domains = buffer.toString().trim();
-
- // Apply DNS changes.
- synchronized (mDnsLock) {
- updateDnsLocked("VPN", iface, addresses, domains, false);
- }
-
- // Temporarily disable the default proxy (not global).
- synchronized (mProxyLock) {
- mDefaultProxyDisabled = true;
- if (mGlobalProxy == null && mDefaultProxy != null) {
- sendProxyBroadcast(null);
- }
- }
-
- // TODO: support proxy per network.
- }
-
- public void restore() {
- synchronized (mProxyLock) {
- mDefaultProxyDisabled = false;
- if (mGlobalProxy == null && mDefaultProxy != null) {
- sendProxyBroadcast(mDefaultProxy);
- }
- }
- }
-
- public void protect(ParcelFileDescriptor socket) {
- try {
- final int mark = mNetd.getMarkForProtect();
- NetworkUtils.markSocket(socket.getFd(), mark);
- } catch (RemoteException e) {
- }
- }
-
- public void setRoutes(String interfaze, List<RouteInfo> routes) {
- for (RouteInfo route : routes) {
- try {
- mNetd.setMarkedForwardingRoute(interfaze, route);
- } catch (RemoteException e) {
- }
- }
- }
-
- public void setMarkedForwarding(String interfaze) {
- try {
- mNetd.setMarkedForwarding(interfaze);
- } catch (RemoteException e) {
- }
- }
-
- public void clearMarkedForwarding(String interfaze) {
- try {
- mNetd.clearMarkedForwarding(interfaze);
- } catch (RemoteException e) {
- }
- }
-
- public void addUserForwarding(String interfaze, int uid, boolean forwardDns) {
- int uidStart = uid * UserHandle.PER_USER_RANGE;
- int uidEnd = uidStart + UserHandle.PER_USER_RANGE - 1;
- addUidForwarding(interfaze, uidStart, uidEnd, forwardDns);
- }
-
- public void clearUserForwarding(String interfaze, int uid, boolean forwardDns) {
- int uidStart = uid * UserHandle.PER_USER_RANGE;
- int uidEnd = uidStart + UserHandle.PER_USER_RANGE - 1;
- clearUidForwarding(interfaze, uidStart, uidEnd, forwardDns);
- }
-
- public void addUidForwarding(String interfaze, int uidStart, int uidEnd,
- boolean forwardDns) {
- try {
- mNetd.setUidRangeRoute(interfaze,uidStart, uidEnd);
- if (forwardDns) mNetd.setDnsInterfaceForUidRange(interfaze, uidStart, uidEnd);
- } catch (RemoteException e) {
- }
-
- }
-
- public void clearUidForwarding(String interfaze, int uidStart, int uidEnd,
- boolean forwardDns) {
- try {
- mNetd.clearUidRangeRoute(interfaze, uidStart, uidEnd);
- if (forwardDns) mNetd.clearDnsInterfaceForUidRange(interfaze, uidStart, uidEnd);
- } catch (RemoteException e) {
- }
-
- }
- }
-
@Override
public boolean updateLockdownVpn() {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
@@ -4179,7 +4421,10 @@
CheckMp.Params params =
new CheckMp.Params(checkMp.getDefaultUrl(), timeOutMs, cb);
if (DBG) log("checkMobileProvisioning: params=" + params);
- checkMp.execute(params);
+ // TODO: Reenable when calls to the now defunct
+ // MobileDataStateTracker.isProvisioningNetwork() are removed.
+ // This code should be moved to the Telephony code.
+ // checkMp.execute(params);
} finally {
Binder.restoreCallingIdentity(token);
if (DBG) log("checkMobileProvisioning: X");
@@ -4422,10 +4667,10 @@
log("isMobileOk: addresses=" + inetAddressesToString(addresses));
// Get the type of addresses supported by this link
- LinkProperties lp = mCs.getLinkProperties(
+ LinkProperties lp = mCs.getLinkPropertiesForTypeInternal(
ConnectivityManager.TYPE_MOBILE_HIPRI);
boolean linkHasIpv4 = lp.hasIPv4Address();
- boolean linkHasIpv6 = lp.hasIPv6Address();
+ boolean linkHasIpv6 = lp.hasGlobalIPv6Address();
log("isMobileOk: linkHasIpv4=" + linkHasIpv4
+ " linkHasIpv6=" + linkHasIpv6);
@@ -4631,10 +4876,13 @@
* @param seconds
*/
private static void sleep(int seconds) {
- try {
- Thread.sleep(seconds * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
+ long stopTime = System.nanoTime() + (seconds * 1000000000);
+ long sleepTime;
+ while ((sleepTime = stopTime - System.nanoTime()) > 0) {
+ try {
+ Thread.sleep(sleepTime / 1000000);
+ } catch (InterruptedException ignored) {
+ }
}
}
@@ -4668,25 +4916,37 @@
if (mIsProvisioningNetwork.get() && !isAirplaneModeOn) {
if (DBG) log("handleMobileProvisioningAction: on prov network enable then launch");
mIsProvisioningNetwork.set(false);
- mIsStartingProvisioning.set(true);
- MobileDataStateTracker mdst = (MobileDataStateTracker)
- mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+// mIsStartingProvisioning.set(true);
+// MobileDataStateTracker mdst = (MobileDataStateTracker)
+// mNetTrackers[ConnectivityManager.TYPE_MOBILE];
// Radio was disabled on CMP_RESULT_CODE_PROVISIONING_NETWORK, enable it here
- mdst.setRadio(true);
- mdst.setEnableFailFastMobileData(DctConstants.ENABLED);
- mdst.enableMobileProvisioning(url);
+// mdst.setRadio(true);
+// mdst.setEnableFailFastMobileData(DctConstants.ENABLED);
+// mdst.enableMobileProvisioning(url);
} else {
- if (DBG) log("handleMobileProvisioningAction: not prov network, launch browser directly");
+ if (DBG) log("handleMobileProvisioningAction: not prov network");
mIsProvisioningNetwork.set(false);
- Intent newIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
- Intent.CATEGORY_APP_BROWSER);
- newIntent.setData(Uri.parse(url));
- newIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
- Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- mContext.startActivity(newIntent);
- } catch (ActivityNotFoundException e) {
- loge("handleMobileProvisioningAction: startActivity failed" + e);
+ // Check for apps that can handle provisioning first
+ Intent provisioningIntent = new Intent(TelephonyIntents.ACTION_CARRIER_SETUP);
+ provisioningIntent.addCategory(TelephonyIntents.CATEGORY_MCCMNC_PREFIX
+ + mTelephonyManager.getSimOperator());
+ if (mContext.getPackageManager().resolveActivity(provisioningIntent, 0 /* flags */)
+ != null) {
+ provisioningIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+ Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(provisioningIntent);
+ } else {
+ // If no apps exist, use standard URL ACTION_VIEW method
+ Intent newIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
+ Intent.CATEGORY_APP_BROWSER);
+ newIntent.setData(Uri.parse(url));
+ newIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+ Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ mContext.startActivity(newIntent);
+ } catch (ActivityNotFoundException e) {
+ loge("handleMobileProvisioningAction: startActivity failed" + e);
+ }
}
}
}
@@ -4700,6 +4960,40 @@
log("setProvNotificationVisible: E visible=" + visible + " networkType=" + networkType
+ " extraInfo=" + extraInfo + " url=" + url);
}
+ Intent intent = null;
+ PendingIntent pendingIntent = null;
+ if (visible) {
+ switch (networkType) {
+ case ConnectivityManager.TYPE_WIFI:
+ intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+ Intent.FLAG_ACTIVITY_NEW_TASK);
+ pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+ break;
+ case ConnectivityManager.TYPE_MOBILE:
+ case ConnectivityManager.TYPE_MOBILE_HIPRI:
+ intent = new Intent(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
+ intent.putExtra("EXTRA_URL", url);
+ intent.setFlags(0);
+ pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ break;
+ default:
+ intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+ Intent.FLAG_ACTIVITY_NEW_TASK);
+ pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+ break;
+ }
+ }
+ setProvNotificationVisibleIntent(visible, networkType, extraInfo, pendingIntent);
+ }
+
+ private void setProvNotificationVisibleIntent(boolean visible, int networkType,
+ String extraInfo, PendingIntent intent) {
+ if (DBG) {
+ log("setProvNotificationVisibleIntent: E visible=" + visible + " networkType=" +
+ networkType + " extraInfo=" + extraInfo);
+ }
Resources r = Resources.getSystem();
NotificationManager notificationManager = (NotificationManager) mContext
@@ -4709,7 +5003,6 @@
CharSequence title;
CharSequence details;
int icon;
- Intent intent;
Notification notification = new Notification();
switch (networkType) {
case ConnectivityManager.TYPE_WIFI:
@@ -4717,10 +5010,6 @@
details = r.getString(R.string.network_available_sign_in_detailed,
extraInfo);
icon = R.drawable.stat_notify_wifi_in_range;
- intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
- Intent.FLAG_ACTIVITY_NEW_TASK);
- notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
break;
case ConnectivityManager.TYPE_MOBILE:
case ConnectivityManager.TYPE_MOBILE_HIPRI:
@@ -4729,20 +5018,12 @@
// name has been added to it
details = mTelephonyManager.getNetworkOperatorName();
icon = R.drawable.stat_notify_rssi_in_range;
- intent = new Intent(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
- intent.putExtra("EXTRA_URL", url);
- intent.setFlags(0);
- notification.contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
break;
default:
title = r.getString(R.string.network_available_sign_in, 0);
details = r.getString(R.string.network_available_sign_in_detailed,
extraInfo);
icon = R.drawable.stat_notify_rssi_in_range;
- intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
- Intent.FLAG_ACTIVITY_NEW_TASK);
- notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
break;
}
@@ -4751,6 +5032,7 @@
notification.flags = Notification.FLAG_AUTO_CANCEL;
notification.tickerText = title;
notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
+ notification.contentIntent = intent;
try {
notificationManager.notify(NOTIFICATION_ID, networkType, notification);
@@ -4924,9 +5206,8 @@
loge("Starting user already has a VPN");
return;
}
- userVpn = new Vpn(mContext, mVpnCallback, mNetd, this, userId);
+ userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, this, userId);
mVpns.put(userId, userVpn);
- userVpn.startMonitoring(mContext, mTrackerHandler);
}
}
@@ -4959,7 +5240,7 @@
@Override
public LinkQualityInfo getLinkQualityInfo(int networkType) {
enforceAccessPermission();
- if (isNetworkTypeValid(networkType)) {
+ if (isNetworkTypeValid(networkType) && mNetTrackers[networkType] != null) {
return mNetTrackers[networkType].getLinkQualityInfo();
} else {
return null;
@@ -4969,7 +5250,8 @@
@Override
public LinkQualityInfo getActiveLinkQualityInfo() {
enforceAccessPermission();
- if (isNetworkTypeValid(mActiveDefaultNetwork)) {
+ if (isNetworkTypeValid(mActiveDefaultNetwork) &&
+ mNetTrackers[mActiveDefaultNetwork] != null) {
return mNetTrackers[mActiveDefaultNetwork].getLinkQualityInfo();
} else {
return null;
@@ -5052,4 +5334,744 @@
}
mAlarmManager.set(alarmType, wakeupTime, intent);
}
+
+ private final HashMap<Messenger, NetworkFactoryInfo> mNetworkFactoryInfos =
+ new HashMap<Messenger, NetworkFactoryInfo>();
+ private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests =
+ new HashMap<NetworkRequest, NetworkRequestInfo>();
+
+ private static class NetworkFactoryInfo {
+ public final String name;
+ public final Messenger messenger;
+ public final AsyncChannel asyncChannel;
+
+ public NetworkFactoryInfo(String name, Messenger messenger, AsyncChannel asyncChannel) {
+ this.name = name;
+ this.messenger = messenger;
+ this.asyncChannel = asyncChannel;
+ }
+ }
+
+ private class NetworkRequestInfo implements IBinder.DeathRecipient {
+ static final boolean REQUEST = true;
+ static final boolean LISTEN = false;
+
+ final NetworkRequest request;
+ IBinder mBinder;
+ final int mPid;
+ final int mUid;
+ final Messenger messenger;
+ final boolean isRequest;
+
+ NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, boolean isRequest) {
+ super();
+ messenger = m;
+ request = r;
+ mBinder = binder;
+ mPid = getCallingPid();
+ mUid = getCallingUid();
+ this.isRequest = isRequest;
+
+ try {
+ mBinder.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ binderDied();
+ }
+ }
+
+ void unlinkDeathRecipient() {
+ mBinder.unlinkToDeath(this, 0);
+ }
+
+ public void binderDied() {
+ log("ConnectivityService NetworkRequestInfo binderDied(" +
+ request + ", " + mBinder + ")");
+ releaseNetworkRequest(request);
+ }
+
+ public String toString() {
+ return (isRequest ? "Request" : "Listen") + " from uid/pid:" + mUid + "/" +
+ mPid + " for " + request;
+ }
+ }
+
+ @Override
+ public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
+ Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
+ if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ == false) {
+ enforceConnectivityInternalPermission();
+ } else {
+ enforceChangePermission();
+ }
+
+ if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) {
+ throw new IllegalArgumentException("Bad timeout specified");
+ }
+ NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities(
+ networkCapabilities), legacyType, nextNetworkRequestId());
+ if (DBG) log("requestNetwork for " + networkRequest);
+ NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
+ NetworkRequestInfo.REQUEST);
+
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri));
+ if (timeoutMs > 0) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NETWORK_REQUEST,
+ nri), timeoutMs);
+ }
+ return networkRequest;
+ }
+
+ @Override
+ public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities,
+ PendingIntent operation) {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
+ Messenger messenger, IBinder binder) {
+ enforceAccessPermission();
+
+ NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities(
+ networkCapabilities), TYPE_NONE, nextNetworkRequestId());
+ if (DBG) log("listenForNetwork for " + networkRequest);
+ NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
+ NetworkRequestInfo.LISTEN);
+
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
+ return networkRequest;
+ }
+
+ @Override
+ public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
+ PendingIntent operation) {
+ }
+
+ @Override
+ public void releaseNetworkRequest(NetworkRequest networkRequest) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(),
+ 0, networkRequest));
+ }
+
+ @Override
+ public void registerNetworkFactory(Messenger messenger, String name) {
+ enforceConnectivityInternalPermission();
+ NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, new AsyncChannel());
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi));
+ }
+
+ private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) {
+ if (VDBG) log("Got NetworkFactory Messenger for " + nfi.name);
+ mNetworkFactoryInfos.put(nfi.messenger, nfi);
+ nfi.asyncChannel.connect(mContext, mTrackerHandler, nfi.messenger);
+ }
+
+ @Override
+ public void unregisterNetworkFactory(Messenger messenger) {
+ enforceConnectivityInternalPermission();
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_FACTORY, messenger));
+ }
+
+ private void handleUnregisterNetworkFactory(Messenger messenger) {
+ NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(messenger);
+ if (nfi == null) {
+ if (VDBG) log("Failed to find Messenger in unregisterNetworkFactory");
+ return;
+ }
+ if (VDBG) log("unregisterNetworkFactory for " + nfi.name);
+ }
+
+ /**
+ * NetworkAgentInfo supporting a request by requestId.
+ * These have already been vetted (their Capabilities satisfy the request)
+ * and the are the highest scored network available.
+ * the are keyed off the Requests requestId.
+ */
+ private final SparseArray<NetworkAgentInfo> mNetworkForRequestId =
+ new SparseArray<NetworkAgentInfo>();
+
+ private final SparseArray<NetworkAgentInfo> mNetworkForNetId =
+ new SparseArray<NetworkAgentInfo>();
+
+ // NetworkAgentInfo keyed off its connecting messenger
+ // TODO - eval if we can reduce the number of lists/hashmaps/sparsearrays
+ private final HashMap<Messenger, NetworkAgentInfo> mNetworkAgentInfos =
+ new HashMap<Messenger, NetworkAgentInfo>();
+
+ private final NetworkRequest mDefaultRequest;
+
+ public void registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
+ LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
+ int currentScore) {
+ enforceConnectivityInternalPermission();
+
+ NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), nextNetId(),
+ new NetworkInfo(networkInfo), new LinkProperties(linkProperties),
+ new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler);
+ if (VDBG) log("registerNetworkAgent " + nai);
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
+ }
+
+ private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
+ if (VDBG) log("Got NetworkAgent Messenger");
+ mNetworkAgentInfos.put(na.messenger, na);
+ synchronized (mNetworkForNetId) {
+ mNetworkForNetId.put(na.network.netId, na);
+ }
+ na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
+ NetworkInfo networkInfo = na.networkInfo;
+ na.networkInfo = null;
+ updateNetworkInfo(na, networkInfo);
+ }
+
+ private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) {
+ LinkProperties newLp = networkAgent.linkProperties;
+ int netId = networkAgent.network.netId;
+
+ updateInterfaces(newLp, oldLp, netId);
+ updateMtu(newLp, oldLp);
+ // TODO - figure out what to do for clat
+// for (LinkProperties lp : newLp.getStackedLinks()) {
+// updateMtu(lp, null);
+// }
+ updateRoutes(newLp, oldLp, netId);
+ updateDnses(newLp, oldLp, netId);
+ updateClat(newLp, oldLp, networkAgent);
+ }
+
+ private void updateClat(LinkProperties newLp, LinkProperties oldLp, NetworkAgentInfo na) {
+ // Update 464xlat state.
+ if (mClat.requiresClat(na)) {
+
+ // If the connection was previously using clat, but is not using it now, stop the clat
+ // daemon. Normally, this happens automatically when the connection disconnects, but if
+ // the disconnect is not reported, or if the connection's LinkProperties changed for
+ // some other reason (e.g., handoff changes the IP addresses on the link), it would
+ // still be running. If it's not running, then stopping it is a no-op.
+ if (Nat464Xlat.isRunningClat(oldLp) && !Nat464Xlat.isRunningClat(newLp)) {
+ mClat.stopClat();
+ }
+ // If the link requires clat to be running, then start the daemon now.
+ if (na.networkInfo.isConnected()) {
+ mClat.startClat(na);
+ } else {
+ mClat.stopClat();
+ }
+ }
+ }
+
+ private void updateInterfaces(LinkProperties newLp, LinkProperties oldLp, int netId) {
+ CompareResult<String> interfaceDiff = new CompareResult<String>();
+ if (oldLp != null) {
+ interfaceDiff = oldLp.compareAllInterfaceNames(newLp);
+ } else if (newLp != null) {
+ interfaceDiff.added = newLp.getAllInterfaceNames();
+ }
+ for (String iface : interfaceDiff.added) {
+ try {
+ mNetd.addInterfaceToNetwork(iface, netId);
+ } catch (Exception e) {
+ loge("Exception adding interface: " + e);
+ }
+ }
+ for (String iface : interfaceDiff.removed) {
+ try {
+ mNetd.removeInterfaceFromNetwork(iface, netId);
+ } catch (Exception e) {
+ loge("Exception removing interface: " + e);
+ }
+ }
+ }
+
+ private void updateRoutes(LinkProperties newLp, LinkProperties oldLp, int netId) {
+ CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>();
+ if (oldLp != null) {
+ routeDiff = oldLp.compareAllRoutes(newLp);
+ } else if (newLp != null) {
+ routeDiff.added = newLp.getAllRoutes();
+ }
+
+ // add routes before removing old in case it helps with continuous connectivity
+
+ // do this twice, adding non-nexthop routes first, then routes they are dependent on
+ for (RouteInfo route : routeDiff.added) {
+ if (route.hasGateway()) continue;
+ try {
+ mNetd.addRoute(netId, route);
+ } catch (Exception e) {
+ loge("Exception in addRoute for non-gateway: " + e);
+ }
+ }
+ for (RouteInfo route : routeDiff.added) {
+ if (route.hasGateway() == false) continue;
+ try {
+ mNetd.addRoute(netId, route);
+ } catch (Exception e) {
+ loge("Exception in addRoute for gateway: " + e);
+ }
+ }
+
+ for (RouteInfo route : routeDiff.removed) {
+ try {
+ mNetd.removeRoute(netId, route);
+ } catch (Exception e) {
+ loge("Exception in removeRoute: " + e);
+ }
+ }
+ }
+ private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId) {
+ if (oldLp == null || (newLp.isIdenticalDnses(oldLp) == false)) {
+ Collection<InetAddress> dnses = newLp.getDnsServers();
+ if (dnses.size() == 0 && mDefaultDns != null) {
+ dnses = new ArrayList();
+ dnses.add(mDefaultDns);
+ if (DBG) {
+ loge("no dns provided for netId " + netId + ", so using defaults");
+ }
+ }
+ try {
+ mNetd.setDnsServersForNetwork(netId, NetworkUtils.makeStrings(dnses),
+ newLp.getDomains());
+ } catch (Exception e) {
+ loge("Exception in setDnsServersForNetwork: " + e);
+ }
+ NetworkAgentInfo defaultNai = mNetworkForRequestId.get(mDefaultRequest.requestId);
+ if (defaultNai != null && defaultNai.network.netId == netId) {
+ setDefaultDnsSystemProperties(dnses);
+ }
+ }
+ }
+
+ private void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
+ int last = 0;
+ for (InetAddress dns : dnses) {
+ ++last;
+ String key = "net.dns" + last;
+ String value = dns.getHostAddress();
+ SystemProperties.set(key, value);
+ }
+ for (int i = last + 1; i <= mNumDnsEntries; ++i) {
+ String key = "net.dns" + i;
+ SystemProperties.set(key, "");
+ }
+ mNumDnsEntries = last;
+ }
+
+
+ private void updateCapabilities(NetworkAgentInfo networkAgent,
+ NetworkCapabilities networkCapabilities) {
+ // TODO - what else here? Verify still satisfies everybody?
+ // Check if satisfies somebody new? call callbacks?
+ synchronized (networkAgent) {
+ networkAgent.networkCapabilities = networkCapabilities;
+ }
+ }
+
+ private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) {
+ if (VDBG) log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
+ for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
+ nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, 0,
+ networkRequest);
+ }
+ }
+
+ private void callCallbackForRequest(NetworkRequestInfo nri,
+ NetworkAgentInfo networkAgent, int notificationType) {
+ if (nri.messenger == null) return; // Default request has no msgr
+ Object o;
+ int a1 = 0;
+ int a2 = 0;
+ switch (notificationType) {
+ case ConnectivityManager.CALLBACK_LOSING:
+ a1 = 30 * 1000; // TODO - read this from NetworkMonitor
+ // fall through
+ case ConnectivityManager.CALLBACK_PRECHECK:
+ case ConnectivityManager.CALLBACK_AVAILABLE:
+ case ConnectivityManager.CALLBACK_LOST:
+ case ConnectivityManager.CALLBACK_CAP_CHANGED:
+ case ConnectivityManager.CALLBACK_IP_CHANGED: {
+ o = new NetworkRequest(nri.request);
+ a2 = networkAgent.network.netId;
+ break;
+ }
+ case ConnectivityManager.CALLBACK_UNAVAIL:
+ case ConnectivityManager.CALLBACK_RELEASED: {
+ o = new NetworkRequest(nri.request);
+ break;
+ }
+ default: {
+ loge("Unknown notificationType " + notificationType);
+ return;
+ }
+ }
+ Message msg = Message.obtain();
+ msg.arg1 = a1;
+ msg.arg2 = a2;
+ msg.obj = o;
+ msg.what = notificationType;
+ try {
+ if (VDBG) log("sending notification " + notificationType + " for " + nri.request);
+ nri.messenger.send(msg);
+ } catch (RemoteException e) {
+ // may occur naturally in the race of binder death.
+ loge("RemoteException caught trying to send a callback msg for " + nri.request);
+ }
+ }
+
+ private void handleLingerComplete(NetworkAgentInfo oldNetwork) {
+ if (oldNetwork == null) {
+ loge("Unknown NetworkAgentInfo in handleLingerComplete");
+ return;
+ }
+ if (DBG) log("handleLingerComplete for " + oldNetwork.name());
+ if (DBG) {
+ if (oldNetwork.networkRequests.size() != 0) {
+ loge("Dead network still had " + oldNetwork.networkRequests.size() + " requests");
+ }
+ }
+ oldNetwork.asyncChannel.disconnect();
+ }
+
+ private void handleConnectionValidated(NetworkAgentInfo newNetwork) {
+ if (newNetwork == null) {
+ loge("Unknown NetworkAgentInfo in handleConnectionValidated");
+ return;
+ }
+ boolean keep = newNetwork.isVPN();
+ boolean isNewDefault = false;
+ if (DBG) log("handleConnectionValidated for "+newNetwork.name());
+ // check if any NetworkRequest wants this NetworkAgent
+ ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<NetworkAgentInfo>();
+ if (VDBG) log(" new Network has: " + newNetwork.networkCapabilities);
+ for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+ NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
+ if (newNetwork == currentNetwork) {
+ if (VDBG) log("Network " + newNetwork.name() + " was already satisfying" +
+ " request " + nri.request.requestId + ". No change.");
+ keep = true;
+ continue;
+ }
+
+ // check if it satisfies the NetworkCapabilities
+ if (VDBG) log(" checking if request is satisfied: " + nri.request);
+ if (nri.request.networkCapabilities.satisfiedByNetworkCapabilities(
+ newNetwork.networkCapabilities)) {
+ // next check if it's better than any current network we're using for
+ // this request
+ if (VDBG) {
+ log("currentScore = " +
+ (currentNetwork != null ? currentNetwork.currentScore : 0) +
+ ", newScore = " + newNetwork.currentScore);
+ }
+ if (currentNetwork == null ||
+ currentNetwork.currentScore < newNetwork.currentScore) {
+ if (currentNetwork != null) {
+ if (VDBG) log(" accepting network in place of " + currentNetwork.name());
+ currentNetwork.networkRequests.remove(nri.request.requestId);
+ currentNetwork.networkLingered.add(nri.request);
+ affectedNetworks.add(currentNetwork);
+ } else {
+ if (VDBG) log(" accepting network in place of null");
+ }
+ mNetworkForRequestId.put(nri.request.requestId, newNetwork);
+ newNetwork.addRequest(nri.request);
+ int legacyType = nri.request.legacyType;
+ if (legacyType != TYPE_NONE) {
+ mLegacyTypeTracker.add(legacyType, newNetwork);
+ }
+ keep = true;
+ // TODO - this could get expensive if we have alot of requests for this
+ // network. Think about if there is a way to reduce this. Push
+ // netid->request mapping to each factory?
+ sendUpdatedScoreToFactories(nri.request, newNetwork.currentScore);
+ if (mDefaultRequest.requestId == nri.request.requestId) {
+ isNewDefault = true;
+ updateActiveDefaultNetwork(newNetwork);
+ if (newNetwork.linkProperties != null) {
+ setDefaultDnsSystemProperties(
+ newNetwork.linkProperties.getDnsServers());
+ } else {
+ setDefaultDnsSystemProperties(new ArrayList<InetAddress>());
+ }
+ mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork);
+ }
+ }
+ }
+ }
+ for (NetworkAgentInfo nai : affectedNetworks) {
+ boolean teardown = !nai.isVPN();
+ for (int i = 0; i < nai.networkRequests.size() && teardown; i++) {
+ NetworkRequest nr = nai.networkRequests.valueAt(i);
+ try {
+ if (mNetworkRequests.get(nr).isRequest) {
+ teardown = false;
+ }
+ } catch (Exception e) {
+ loge("Request " + nr + " not found in mNetworkRequests.");
+ loge(" it came from request list of " + nai.name());
+ }
+ }
+ if (teardown) {
+ nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_LINGER);
+ notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING);
+ } else {
+ // not going to linger, so kill the list of linger networks.. only
+ // notify them of linger if it happens as the result of gaining another,
+ // but if they transition and old network stays up, don't tell them of linger
+ // or very delayed loss
+ nai.networkLingered.clear();
+ if (VDBG) log("Lingered for " + nai.name() + " cleared");
+ }
+ }
+ if (keep) {
+ if (isNewDefault) {
+ if (VDBG) log("Switching to new default network: " + newNetwork);
+ setupDataActivityTracking(newNetwork);
+ try {
+ mNetd.setDefaultNetId(newNetwork.network.netId);
+ } catch (Exception e) {
+ loge("Exception setting default network :" + e);
+ }
+ if (newNetwork.equals(mNetworkForRequestId.get(mDefaultRequest.requestId))) {
+ handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
+ }
+ synchronized (ConnectivityService.this) {
+ // have a new default network, release the transition wakelock in
+ // a second if it's held. The second pause is to allow apps
+ // to reconnect over the new network
+ if (mNetTransitionWakeLock.isHeld()) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
+ mNetTransitionWakeLockSerialNumber, 0),
+ 1000);
+ }
+ }
+
+ // this will cause us to come up initially as unconnected and switching
+ // to connected after our normal pause unless somebody reports us as
+ // really disconnected
+ mDefaultInetConditionPublished = 0;
+ mDefaultConnectionSequence++;
+ mInetConditionChangeInFlight = false;
+ // TODO - read the tcp buffer size config string from somewhere
+ // updateNetworkSettings();
+ }
+ // notify battery stats service about this network
+ try {
+ BatteryStatsService.getService().noteNetworkInterfaceType(
+ newNetwork.linkProperties.getInterfaceName(),
+ newNetwork.networkInfo.getType());
+ } catch (RemoteException e) { }
+ notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_AVAILABLE);
+ } else {
+ if (DBG && newNetwork.networkRequests.size() != 0) {
+ loge("tearing down network with live requests:");
+ for (int i=0; i < newNetwork.networkRequests.size(); i++) {
+ loge(" " + newNetwork.networkRequests.valueAt(i));
+ }
+ }
+ if (VDBG) log("Validated network turns out to be unwanted. Tear it down.");
+ newNetwork.asyncChannel.disconnect();
+ }
+ }
+
+
+ private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
+ NetworkInfo.State state = newInfo.getState();
+ NetworkInfo oldInfo = null;
+ synchronized (networkAgent) {
+ oldInfo = networkAgent.networkInfo;
+ networkAgent.networkInfo = newInfo;
+ }
+ if (networkAgent.isVPN() && mLockdownTracker != null) {
+ mLockdownTracker.onVpnStateChanged(newInfo);
+ }
+
+ if (oldInfo != null && oldInfo.getState() == state) {
+ if (VDBG) log("ignoring duplicate network state non-change");
+ return;
+ }
+ if (DBG) {
+ log(networkAgent.name() + " EVENT_NETWORK_INFO_CHANGED, going from " +
+ (oldInfo == null ? "null" : oldInfo.getState()) +
+ " to " + state);
+ }
+
+ if (state == NetworkInfo.State.CONNECTED) {
+ try {
+ // This is likely caused by the fact that this network already
+ // exists. An example is when a network goes from CONNECTED to
+ // CONNECTING and back (like wifi on DHCP renew).
+ // TODO: keep track of which networks we've created, or ask netd
+ // to tell us whether we've already created this network or not.
+ if (networkAgent.isVPN()) {
+ mNetd.createVirtualNetwork(networkAgent.network.netId,
+ !networkAgent.linkProperties.getDnsServers().isEmpty());
+ } else {
+ mNetd.createPhysicalNetwork(networkAgent.network.netId);
+ }
+ } catch (Exception e) {
+ loge("Error creating network " + networkAgent.network.netId + ": "
+ + e.getMessage());
+ return;
+ }
+
+ updateLinkProperties(networkAgent, null);
+ notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
+ networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
+ if (networkAgent.isVPN()) {
+ // Temporarily disable the default proxy (not global).
+ synchronized (mProxyLock) {
+ if (!mDefaultProxyDisabled) {
+ mDefaultProxyDisabled = true;
+ if (mGlobalProxy == null && mDefaultProxy != null) {
+ sendProxyBroadcast(null);
+ }
+ }
+ }
+ // TODO: support proxy per network.
+ }
+ } else if (state == NetworkInfo.State.DISCONNECTED ||
+ state == NetworkInfo.State.SUSPENDED) {
+ networkAgent.asyncChannel.disconnect();
+ if (networkAgent.isVPN()) {
+ synchronized (mProxyLock) {
+ if (mDefaultProxyDisabled) {
+ mDefaultProxyDisabled = false;
+ if (mGlobalProxy == null && mDefaultProxy != null) {
+ sendProxyBroadcast(mDefaultProxy);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void updateNetworkScore(NetworkAgentInfo nai, int score) {
+ if (DBG) log("updateNetworkScore for " + nai.name() + " to " + score);
+
+ nai.currentScore = score;
+
+ // TODO - This will not do the right thing if this network is lowering
+ // its score and has requests that can be served by other
+ // currently-active networks, or if the network is increasing its
+ // score and other networks have requests that can be better served
+ // by this network.
+ //
+ // Really we want to see if any of our requests migrate to other
+ // active/lingered networks and if any other requests migrate to us (depending
+ // on increasing/decreasing currentScore. That's a bit of work and probably our
+ // score checking/network allocation code needs to be modularized so we can understand
+ // (see handleConnectionValided for an example).
+ //
+ // As a first order approx, lets just advertise the new score to factories. If
+ // somebody can beat it they will nominate a network and our normal net replacement
+ // code will fire.
+ for (int i = 0; i < nai.networkRequests.size(); i++) {
+ NetworkRequest nr = nai.networkRequests.valueAt(i);
+ sendUpdatedScoreToFactories(nr, score);
+ }
+ }
+
+ // notify only this one new request of the current state
+ protected void notifyNetworkCallback(NetworkAgentInfo nai, NetworkRequestInfo nri) {
+ int notifyType = ConnectivityManager.CALLBACK_AVAILABLE;
+ // TODO - read state from monitor to decide what to send.
+// if (nai.networkMonitor.isLingering()) {
+// notifyType = NetworkCallbacks.LOSING;
+// } else if (nai.networkMonitor.isEvaluating()) {
+// notifyType = NetworkCallbacks.callCallbackForRequest(request, nai, notifyType);
+// }
+ callCallbackForRequest(nri, nai, notifyType);
+ }
+
+ private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, boolean connected, int type) {
+ if (connected) {
+ NetworkInfo info = new NetworkInfo(nai.networkInfo);
+ info.setType(type);
+ sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
+ } else {
+ NetworkInfo info = new NetworkInfo(nai.networkInfo);
+ info.setType(type);
+ Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
+ if (info.isFailover()) {
+ intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
+ nai.networkInfo.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());
+ }
+ NetworkAgentInfo newDefaultAgent = null;
+ if (nai.networkRequests.get(mDefaultRequest.requestId) != null) {
+ newDefaultAgent = mNetworkForRequestId.get(mDefaultRequest.requestId);
+ if (newDefaultAgent != null) {
+ intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
+ newDefaultAgent.networkInfo);
+ } else {
+ intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
+ }
+ }
+ intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION,
+ mDefaultInetConditionPublished);
+ final Intent immediateIntent = new Intent(intent);
+ immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
+ sendStickyBroadcast(immediateIntent);
+ sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay());
+ if (newDefaultAgent != null) {
+ sendConnectedBroadcastDelayed(newDefaultAgent.networkInfo,
+ getConnectivityChangeDelay());
+ }
+ }
+ }
+
+ protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) {
+ if (VDBG) log("notifyType " + notifyType + " for " + networkAgent.name());
+ for (int i = 0; i < networkAgent.networkRequests.size(); i++) {
+ NetworkRequest nr = networkAgent.networkRequests.valueAt(i);
+ NetworkRequestInfo nri = mNetworkRequests.get(nr);
+ if (VDBG) log(" sending notification for " + nr);
+ callCallbackForRequest(nri, networkAgent, notifyType);
+ }
+ }
+
+ private LinkProperties getLinkPropertiesForTypeInternal(int networkType) {
+ NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+ if (nai != null) {
+ synchronized (nai) {
+ return new LinkProperties(nai.linkProperties);
+ }
+ }
+ return new LinkProperties();
+ }
+
+ private NetworkInfo getNetworkInfoForType(int networkType) {
+ if (!mLegacyTypeTracker.isTypeSupported(networkType))
+ return null;
+
+ NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+ if (nai != null) {
+ NetworkInfo result = new NetworkInfo(nai.networkInfo);
+ result.setType(networkType);
+ return result;
+ } else {
+ return new NetworkInfo(networkType, 0, "Unknown", "");
+ }
+ }
+
+ private NetworkCapabilities getNetworkCapabilitiesForType(int networkType) {
+ NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+ if (nai != null) {
+ synchronized (nai) {
+ return new NetworkCapabilities(nai.networkCapabilities);
+ }
+ }
+ return new NetworkCapabilities();
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index a15d678..096ab66 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -25,11 +25,12 @@
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.LinkProperties;
-import android.net.NetworkStateTracker;
+import android.net.NetworkAgent;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.os.Handler;
import android.os.Message;
+import android.os.Messenger;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.util.Slog;
@@ -45,15 +46,18 @@
private Context mContext;
private INetworkManagementService mNMService;
private IConnectivityManager mConnService;
- private NetworkStateTracker mTracker;
- private Handler mHandler;
-
// Whether we started clatd and expect it to be running.
private boolean mIsStarted;
// Whether the clatd interface exists (i.e., clatd is running).
private boolean mIsRunning;
// The LinkProperties of the clat interface.
private LinkProperties mLP;
+ // Current LinkProperties of the network. Includes mLP as a stacked link when clat is active.
+ private LinkProperties mBaseLP;
+ // ConnectivityService Handler for LinkProperties updates.
+ private Handler mHandler;
+ // Marker to connote which network we're augmenting.
+ private Messenger mNetworkMessenger;
// This must match the interface name in clatd.conf.
private static final String CLAT_INTERFACE_NAME = "clat4";
@@ -70,17 +74,24 @@
mIsStarted = false;
mIsRunning = false;
mLP = new LinkProperties();
+
+ // If this is a runtime restart, it's possible that clatd is already
+ // running, but we don't know about it. If so, stop it.
+ try {
+ if (mNMService.isClatdStarted()) {
+ mNMService.stopClatd();
+ }
+ } catch(RemoteException e) {} // Well, we tried.
}
/**
- * Determines whether an interface requires clat.
- * @param netType the network type (one of the
- * android.net.ConnectivityManager.TYPE_* constants)
- * @param tracker the NetworkStateTracker corresponding to the network type.
- * @return true if the interface requires clat, false otherwise.
+ * Determines whether a network requires clat.
+ * @param network the NetworkAgentInfo corresponding to the network.
+ * @return true if the network requires clat, false otherwise.
*/
- public boolean requiresClat(int netType, NetworkStateTracker tracker) {
- LinkProperties lp = tracker.getLinkProperties();
+ public boolean requiresClat(NetworkAgentInfo network) {
+ int netType = network.networkInfo.getType();
+ LinkProperties lp = network.linkProperties;
// Only support clat on mobile for now.
Slog.d(TAG, "requiresClat: netType=" + netType + ", hasIPv4Address=" +
lp.hasIPv4Address());
@@ -95,13 +106,18 @@
* Starts the clat daemon.
* @param lp The link properties of the interface to start clatd on.
*/
- public void startClat(NetworkStateTracker tracker) {
+ public void startClat(NetworkAgentInfo network) {
+ if (mNetworkMessenger != null && mNetworkMessenger != network.messenger) {
+ Slog.e(TAG, "startClat: too many networks requesting clat");
+ return;
+ }
+ mNetworkMessenger = network.messenger;
+ LinkProperties lp = network.linkProperties;
+ mBaseLP = new LinkProperties(lp);
if (mIsStarted) {
Slog.e(TAG, "startClat: already started");
return;
}
- mTracker = tracker;
- LinkProperties lp = mTracker.getLinkProperties();
String iface = lp.getInterfaceName();
Slog.i(TAG, "Starting clatd on " + iface + ", lp=" + lp);
try {
@@ -125,7 +141,8 @@
}
mIsStarted = false;
mIsRunning = false;
- mTracker = null;
+ mNetworkMessenger = null;
+ mBaseLP = null;
mLP.clear();
} else {
Slog.e(TAG, "stopClat: already stopped");
@@ -140,6 +157,14 @@
return mIsRunning;
}
+ private void updateConnectivityService() {
+ Message msg = mHandler.obtainMessage(
+ NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED, mBaseLP);
+ msg.replyTo = mNetworkMessenger;
+ Slog.i(TAG, "sending message to ConnectivityService: " + msg);
+ msg.sendToTarget();
+ }
+
@Override
public void interfaceAdded(String iface) {
if (iface.equals(CLAT_INTERFACE_NAME)) {
@@ -165,19 +190,12 @@
clatAddress.getAddress(), iface);
mLP.addRoute(ipv4Default);
mLP.addLinkAddress(clatAddress);
- mTracker.addStackedLink(mLP);
- Slog.i(TAG, "Adding stacked link. tracker LP: " +
- mTracker.getLinkProperties());
+ mBaseLP.addStackedLink(mLP);
+ Slog.i(TAG, "Adding stacked link. tracker LP: " + mBaseLP);
+ updateConnectivityService();
} catch(RemoteException e) {
Slog.e(TAG, "Error getting link properties: " + e);
}
-
- // Inform ConnectivityService that things have changed.
- Message msg = mHandler.obtainMessage(
- NetworkStateTracker.EVENT_CONFIGURATION_CHANGED,
- mTracker.getNetworkInfo());
- Slog.i(TAG, "sending message to ConnectivityService: " + msg);
- msg.sendToTarget();
}
}
@@ -188,11 +206,12 @@
NetworkUtils.resetConnections(
CLAT_INTERFACE_NAME,
NetworkUtils.RESET_IPV4_ADDRESSES);
+ mBaseLP.removeStackedLink(mLP);
+ updateConnectivityService();
}
Slog.i(TAG, "interface " + CLAT_INTERFACE_NAME +
" removed, mIsRunning = " + mIsRunning + " -> false");
mIsRunning = false;
- mTracker.removeStackedLink(mLP);
mLP.clear();
Slog.i(TAG, "mLP = " + mLP);
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
new file mode 100644
index 0000000..10bdba0
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 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.connectivity;
+
+import android.content.Context;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkRequest;
+import android.os.Handler;
+import android.os.Messenger;
+import android.util.SparseArray;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.server.connectivity.NetworkMonitor;
+
+import java.util.ArrayList;
+
+/**
+ * A bag class used by ConnectivityService for holding a collection of most recent
+ * information published by a particular NetworkAgent as well as the
+ * AsyncChannel/messenger for reaching that NetworkAgent and lists of NetworkRequests
+ * interested in using it.
+ */
+public class NetworkAgentInfo {
+ public NetworkInfo networkInfo;
+ public final Network network;
+ public LinkProperties linkProperties;
+ public NetworkCapabilities networkCapabilities;
+ public int currentScore;
+ public final NetworkMonitor networkMonitor;
+
+ // The list of NetworkRequests being satisfied by this Network.
+ public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
+ public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>();
+
+ public final Messenger messenger;
+ public final AsyncChannel asyncChannel;
+
+ public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, int netId, NetworkInfo info,
+ LinkProperties lp, NetworkCapabilities nc, int score, Context context,
+ Handler handler) {
+ this.messenger = messenger;
+ asyncChannel = ac;
+ network = new Network(netId);
+ networkInfo = info;
+ linkProperties = lp;
+ networkCapabilities = nc;
+ currentScore = score;
+ networkMonitor = new NetworkMonitor(context, handler, this);
+ }
+
+ public void addRequest(NetworkRequest networkRequest) {
+ networkRequests.put(networkRequest.requestId, networkRequest);
+ }
+
+ public boolean isVPN() {
+ return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
+ }
+
+ public String toString() {
+ return "NetworkAgentInfo{ ni{" + networkInfo + "} network{" +
+ network + "} lp{" +
+ linkProperties + "} nc{" +
+ networkCapabilities + "} Score{" + currentScore + "} }";
+ }
+
+ public String name() {
+ return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" +
+ networkInfo.getSubtypeName() + ") - " + network.toString() + "]";
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index f955f4f..88aaafc 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -163,9 +163,10 @@
nextConnBroadcast.get();
// verify that both routes were added and DNS was flushed
- verify(mNetManager).addRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V4));
- verify(mNetManager).addRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V6));
- verify(mNetManager).flushInterfaceDnsCache(MOBILE_IFACE);
+ int mobileNetId = mMobile.tracker.getNetwork().netId;
+ verify(mNetManager).addRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V4));
+ verify(mNetManager).addRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V6));
+ verify(mNetManager).flushNetworkDnsCache(mobileNetId);
}
@@ -200,11 +201,14 @@
nextConnBroadcast.get();
// verify that wifi routes added, and teardown requested
- verify(mNetManager).addRoute(eq(WIFI_IFACE), eq(WIFI_ROUTE_V4));
- verify(mNetManager).addRoute(eq(WIFI_IFACE), eq(WIFI_ROUTE_V6));
- verify(mNetManager).flushInterfaceDnsCache(WIFI_IFACE);
+ int wifiNetId = mWifi.tracker.getNetwork().netId;
+ verify(mNetManager).addRoute(eq(wifiNetId), eq(WIFI_ROUTE_V4));
+ verify(mNetManager).addRoute(eq(wifiNetId), eq(WIFI_ROUTE_V6));
+ verify(mNetManager).flushNetworkDnsCache(wifiNetId);
verify(mMobile.tracker).teardown();
+ int mobileNetId = mMobile.tracker.getNetwork().netId;
+
reset(mNetManager, mMobile.tracker);
// tear down mobile network, as requested
@@ -216,8 +220,8 @@
mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
nextConnBroadcast.get();
- verify(mNetManager).removeRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V4));
- verify(mNetManager).removeRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V6));
+ verify(mNetManager).removeRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V4));
+ verify(mNetManager).removeRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V6));
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
index 7a30d31..0d5daa5 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
@@ -141,14 +141,21 @@
/**
* Interface class activity.
*/
+
sendMessage("613 IfaceClass active rmnet0");
- expectSoon(observer).interfaceClassDataActivityChanged("rmnet0", true);
+ expectSoon(observer).interfaceClassDataActivityChanged("rmnet0", true, 0);
+
+ sendMessage("613 IfaceClass active rmnet0 1234");
+ expectSoon(observer).interfaceClassDataActivityChanged("rmnet0", true, 1234);
sendMessage("613 IfaceClass idle eth0");
- expectSoon(observer).interfaceClassDataActivityChanged("eth0", false);
+ expectSoon(observer).interfaceClassDataActivityChanged("eth0", false, 0);
- sendMessage("613 IfaceClass reallyactive rmnet0");
- expectSoon(observer).interfaceClassDataActivityChanged("rmnet0", false);
+ sendMessage("613 IfaceClass idle eth0 1234");
+ expectSoon(observer).interfaceClassDataActivityChanged("eth0", false, 1234);
+
+ sendMessage("613 IfaceClass reallyactive rmnet0 1234");
+ expectSoon(observer).interfaceClassDataActivityChanged("rmnet0", false, 1234);
sendMessage("613 InterfaceClass reallyactive rmnet0");
// Invalid group.
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index a1af8cb..8aae695 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -47,6 +47,7 @@
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.isA;
+import android.app.AlarmClockInfo;
import android.app.AlarmManager;
import android.app.IAlarmManager;
import android.app.PendingIntent;
@@ -879,7 +880,7 @@
expectLastCall().anyTimes();
mAlarmManager.set(eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), anyLong(),
- isA(PendingIntent.class), isA(WorkSource.class));
+ isA(PendingIntent.class), isA(WorkSource.class), isA(AlarmClockInfo.class));
expectLastCall().atLeastOnce();
mNetManager.setGlobalAlert(anyLong());