Don\'t use framework permission strings for netd permissions. am: 5c36486ea0
am: f51479864b
* commit 'f51479864b9ef8c82e4250cc972f1adee5a142d6':
Don't use framework permission strings for netd permissions.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 7cde005..57706da 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -37,6 +37,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Log;
@@ -71,7 +72,6 @@
*/
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 default connection has either
@@ -101,7 +101,7 @@
/**
* Identical to {@link #CONNECTIVITY_ACTION} broadcast, but sent without any
- * applicable {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY}.
+ * historic {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY}.
*
* @hide
*/
@@ -429,18 +429,6 @@
public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI;
/**
- * Default value for {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY} in
- * milliseconds. This was introduced because IPv6 routes seem to take a
- * moment to settle - trying network activity before the routes are adjusted
- * can lead to packets using the wrong interface or having the wrong IP address.
- * This delay is a bit crude, but in the future hopefully we will have kernel
- * notifications letting us know when it's safe to use the new network.
- *
- * @hide
- */
- public static final int CONNECTIVITY_CHANGE_DELAY_DEFAULT = 3000;
-
- /**
* @hide
*/
public final static int REQUEST_ID_UNSET = 0;
@@ -453,6 +441,13 @@
public static final int NETID_UNSET = 0;
private final IConnectivityManager mService;
+ /**
+ * A kludge to facilitate static access where a Context pointer isn't available, like in the
+ * case of the static set/getProcessDefaultNetwork methods and from the Network class.
+ * TODO: Remove this after deprecating the static methods in favor of non-static methods or
+ * methods that take a Context argument.
+ */
+ private static ConnectivityManager sInstance;
private INetworkManagementService mNMService;
@@ -722,6 +717,19 @@
}
/**
+ * Returns an array of of {@link NetworkCapabilities} objects, representing
+ * the Networks that applications run by the given user will use by default.
+ * @hide
+ */
+ public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
+ try {
+ return mService.getDefaultNetworkCapabilitiesForUser(userId);
+ } 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
@@ -880,14 +888,6 @@
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);
@@ -933,10 +933,8 @@
return -1;
}
- NetworkCallback networkCallback = removeRequestForFeature(netCap);
- if (networkCallback != null) {
+ if (removeRequestForFeature(netCap)) {
Log.d(TAG, "stopUsingNetworkFeature for " + networkType + ", " + feature);
- unregisterNetworkCallback(networkCallback);
}
return 1;
}
@@ -977,60 +975,57 @@
return null;
}
+ /**
+ * Guess what the network request was trying to say so that the resulting
+ * network is accessible via the legacy (deprecated) API such as
+ * requestRouteToHost.
+ * This means we should try to be fairly preceise about transport and
+ * capability but ignore things such as networkSpecifier.
+ * If the request has more than one transport or capability it doesn't
+ * match the old legacy requests (they selected only single transport/capability)
+ * so this function cannot map the request to a single legacy type and
+ * the resulting network will not be available to the legacy APIs.
+ *
+ * TODO - This should be removed when the legacy APIs are removed.
+ */
private int inferLegacyTypeForNetworkCapabilities(NetworkCapabilities netCap) {
if (netCap == null) {
return TYPE_NONE;
}
+
if (!netCap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
return TYPE_NONE;
}
+
+ String type = null;
+ int result = TYPE_NONE;
+
if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
- if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableCBS"))) {
- return TYPE_MOBILE_CBS;
- } else {
- return TYPE_NONE;
- }
+ type = "enableCBS";
+ result = TYPE_MOBILE_CBS;
+ } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
+ type = "enableIMS";
+ result = TYPE_MOBILE_IMS;
+ } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
+ type = "enableFOTA";
+ result = TYPE_MOBILE_FOTA;
+ } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
+ type = "enableDUN";
+ result = TYPE_MOBILE_DUN;
+ } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
+ type = "enableSUPL";
+ result = TYPE_MOBILE_SUPL;
+ } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
+ type = "enableMMS";
+ result = TYPE_MOBILE_MMS;
+ } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ type = "enableHIPRI";
+ result = TYPE_MOBILE_HIPRI;
}
- if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
- if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableIMS"))) {
- return TYPE_MOBILE_IMS;
- } else {
- return TYPE_NONE;
- }
- }
- if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
- if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableFOTA"))) {
- return TYPE_MOBILE_FOTA;
- } else {
- return TYPE_NONE;
- }
- }
- if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
- if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableDUN"))) {
- return TYPE_MOBILE_DUN;
- } else {
- return TYPE_NONE;
- }
- }
- if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
- if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableSUPL"))) {
- return TYPE_MOBILE_SUPL;
- } else {
- return TYPE_NONE;
- }
- }
- if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
- if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableMMS"))) {
- return TYPE_MOBILE_MMS;
- } else {
- return TYPE_NONE;
- }
- }
- if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableHIPRI"))) {
- return TYPE_MOBILE_HIPRI;
- } else {
- return TYPE_NONE;
+ if (type != null) {
+ NetworkCapabilities testCap = networkCapabilitiesForFeature(TYPE_MOBILE, type);
+ if (testCap.equalsNetCapabilities(netCap) && testCap.equalsTransportTypes(netCap)) {
+ return result;
}
}
return TYPE_NONE;
@@ -1071,6 +1066,14 @@
int expireSequenceNumber;
Network currentNetwork;
int delay = -1;
+
+ private void clearDnsBinding() {
+ if (currentNetwork != null) {
+ currentNetwork = null;
+ setProcessDefaultNetworkForHostResolution(null);
+ }
+ }
+
NetworkCallback networkCallback = new NetworkCallback() {
@Override
public void onAvailable(Network network) {
@@ -1080,10 +1083,7 @@
}
@Override
public void onLost(Network network) {
- if (network.equals(currentNetwork)) {
- currentNetwork = null;
- setProcessDefaultNetworkForHostResolution(null);
- }
+ if (network.equals(currentNetwork)) clearDnsBinding();
Log.d(TAG, "startUsingNetworkFeature lost Network:" + network);
}
};
@@ -1112,10 +1112,7 @@
LegacyRequest l = sLegacyRequests.get(netCap);
if (l == null) return;
ourSeqNum = l.expireSequenceNumber;
- if (l.expireSequenceNumber == sequenceNum) {
- unregisterNetworkCallback(l.networkCallback);
- sLegacyRequests.remove(netCap);
- }
+ if (l.expireSequenceNumber == sequenceNum) removeRequestForFeature(netCap);
}
Log.d(TAG, "expireRequest with " + ourSeqNum + ", " + sequenceNum);
}
@@ -1146,12 +1143,15 @@
}
}
- private NetworkCallback removeRequestForFeature(NetworkCapabilities netCap) {
+ private boolean removeRequestForFeature(NetworkCapabilities netCap) {
+ final LegacyRequest l;
synchronized (sLegacyRequests) {
- LegacyRequest l = sLegacyRequests.remove(netCap);
- if (l == null) return null;
- return l.networkCallback;
+ l = sLegacyRequests.remove(netCap);
}
+ if (l == null) return false;
+ unregisterNetworkCallback(l.networkCallback);
+ l.clearDnsBinding();
+ return true;
}
/**
@@ -1259,9 +1259,15 @@
if (b != null) {
try {
ITelephony it = ITelephony.Stub.asInterface(b);
- return it.getDataEnabled();
+ int subId = SubscriptionManager.getDefaultDataSubId();
+ Log.d("ConnectivityManager", "getMobileDataEnabled()+ subId=" + subId);
+ boolean retVal = it.getDataEnabled(subId);
+ Log.d("ConnectivityManager", "getMobileDataEnabled()- subId=" + subId
+ + " retVal=" + retVal);
+ return retVal;
} catch (RemoteException e) { }
}
+ Log.d("ConnectivityManager", "getMobileDataEnabled()- remote exception retVal=false");
return false;
}
@@ -1361,6 +1367,7 @@
*/
public ConnectivityManager(IConnectivityManager service) {
mService = checkNotNull(service, "missing IConnectivityManager");
+ sInstance = this;
}
/** {@hide} */
@@ -1383,6 +1390,18 @@
}
/**
+ * @deprecated - use getSystemService. This is a kludge to support static access in certain
+ * situations where a Context pointer is unavailable.
+ * @hide
+ */
+ public static ConnectivityManager getInstance() {
+ if (sInstance == null) {
+ throw new IllegalStateException("No ConnectivityManager yet constructed");
+ }
+ return sInstance;
+ }
+
+ /**
* Get the set of tetherable, available interfaces. This list is limited by
* device configuration and current interface existence.
*
@@ -1713,20 +1732,26 @@
}
/**
- * 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.
+ * Get the current default HTTP proxy settings. If a global proxy is set it will be returned,
+ * otherwise if this process is bound to a {@link Network} using
+ * {@link #setProcessDefaultNetwork} then that {@code Network}'s proxy is returned, otherwise
+ * the default network's proxy is returned.
*
* @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}
+ * @hide
*/
- public ProxyInfo getProxy() {
+ public ProxyInfo getDefaultProxy() {
+ final Network network = getProcessDefaultNetwork();
+ if (network != null) {
+ final ProxyInfo globalProxy = getGlobalProxy();
+ if (globalProxy != null) return globalProxy;
+ final LinkProperties lp = getLinkProperties(network);
+ if (lp != null) return lp.getHttpProxy();
+ return null;
+ }
try {
- return mService.getProxy();
+ return mService.getDefaultProxy();
} catch (RemoteException e) {
return null;
}
@@ -1889,45 +1914,6 @@
}
/**
- * get the information about a specific network link
- * @hide
- */
- public LinkQualityInfo getLinkQualityInfo(int networkType) {
- try {
- LinkQualityInfo li = mService.getLinkQualityInfo(networkType);
- return li;
- } catch (RemoteException e) {
- return null;
- }
- }
-
- /**
- * get the information of currently active network link
- * @hide
- */
- public LinkQualityInfo getActiveLinkQualityInfo() {
- try {
- LinkQualityInfo li = mService.getActiveLinkQualityInfo();
- return li;
- } catch (RemoteException e) {
- return null;
- }
- }
-
- /**
- * get the information of all network links
- * @hide
- */
- public LinkQualityInfo[] getAllLinkQualityInfo() {
- try {
- LinkQualityInfo[] li = mService.getAllLinkQualityInfo();
- return li;
- } catch (RemoteException e) {
- return null;
- }
- }
-
- /**
* Set sign in error notification to visible or in visible
*
* @param visible
@@ -2346,26 +2332,27 @@
/**
* The lookup key for a {@link Network} object included with the intent after
- * succesfully finding a network for the applications request. Retrieve it with
+ * successfully finding a network for the applications request. Retrieve it with
* {@link android.content.Intent#getParcelableExtra(String)}.
- * @hide
+ * <p>
+ * Note that if you intend to invoke (@link #setProcessDefaultNetwork(Network)) or
+ * {@link Network#openConnection(java.net.URL)} then you must get a
+ * ConnectivityManager instance before doing so.
*/
- public static final String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork";
+ public static final String EXTRA_NETWORK = "android.net.extra.NETWORK";
/**
* The lookup key for a {@link NetworkRequest} object included with the intent after
- * succesfully finding a network for the applications request. Retrieve it with
+ * successfully 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";
+ public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST";
/**
* Request a network to satisfy a set of {@link NetworkCapabilities}.
*
- * This function behavies identically to the version that takes a NetworkCallback, but instead
+ * This function behaves 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.
@@ -2375,8 +2362,8 @@
* <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
+ * extra called {@link #EXTRA_NETWORK} and a {@link NetworkRequest}
+ * typed extra called {@link #EXTRA_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
@@ -2386,21 +2373,46 @@
* 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}.
+ * The request may be released normally by calling
+ * {@link #releaseNetworkRequest(android.app.PendingIntent)}.
*
* @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
+ * comes from {@link PendingIntent#getBroadcast}. Cannot be null.
*/
public void requestNetwork(NetworkRequest request, PendingIntent operation) {
+ checkPendingIntent(operation);
try {
mService.pendingRequestForNetwork(request.networkCapabilities, operation);
} catch (RemoteException e) {}
}
/**
+ * Removes a request made via {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)}
+ * <p>
+ * This method has the same behavior as {@link #unregisterNetworkCallback} with respect to
+ * releasing network resources and disconnecting.
+ *
+ * @param operation A PendingIntent equal (as defined by {@link Intent#filterEquals}) to the
+ * PendingIntent passed to
+ * {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)} with the
+ * corresponding NetworkRequest you'd like to remove. Cannot be null.
+ */
+ public void releaseNetworkRequest(PendingIntent operation) {
+ checkPendingIntent(operation);
+ try {
+ mService.releasePendingNetworkRequest(operation);
+ } catch (RemoteException e) {}
+ }
+
+ private void checkPendingIntent(PendingIntent intent) {
+ if (intent == null) {
+ throw new IllegalArgumentException("PendingIntent cannot be null.");
+ }
+ }
+
+ /**
* 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
@@ -2416,7 +2428,7 @@
/**
* 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},
+ * given {@code NetworkCallback} had previously been used with {@code #requestNetwork},
* any networks that had been connected to only to satisfy that request will be
* disconnected.
*
@@ -2456,6 +2468,9 @@
return true;
}
if (NetworkUtils.bindProcessToNetwork(netId)) {
+ // Set HTTP proxy system properties to match network.
+ // TODO: Deprecate this static method and replace it with a non-static version.
+ Proxy.setHttpProxySystemProperty(getInstance().getDefaultProxy());
// Must flush DNS cache as new network may have different DNS resolutions.
InetAddress.clearDnsCache();
// Must flush socket pool as idle sockets will be bound to previous network and may
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index a983d88..46af112 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -17,7 +17,6 @@
package android.net;
import android.app.PendingIntent;
-import android.net.LinkQualityInfo;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -50,6 +49,7 @@
NetworkInfo[] getAllNetworkInfo();
Network getNetworkForType(int networkType);
Network[] getAllNetworks();
+ NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId);
NetworkInfo getProvisioningOrActiveNetworkInfo();
@@ -68,9 +68,6 @@
boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress);
- /** Policy control over specific {@link NetworkStateTracker}. */
- void setPolicyDataEnable(int networkType, boolean enabled);
-
int tether(String iface);
int untether(String iface);
@@ -103,7 +100,7 @@
void setGlobalProxy(in ProxyInfo p);
- ProxyInfo getProxy();
+ ProxyInfo getDefaultProxy();
void setDataDependency(int networkType, boolean met);
@@ -133,12 +130,6 @@
String getMobileRedirectedProvisioningUrl();
- LinkQualityInfo getLinkQualityInfo(int networkType);
-
- LinkQualityInfo getActiveLinkQualityInfo();
-
- LinkQualityInfo[] getAllLinkQualityInfo();
-
void setProvisioningNotificationVisible(boolean visible, int networkType, in String action);
void setAirplaneMode(boolean enable);
@@ -156,6 +147,8 @@
NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
in PendingIntent operation);
+ void releasePendingNetworkRequest(in PendingIntent operation);
+
NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
in Messenger messenger, in IBinder binder);
@@ -168,4 +161,5 @@
boolean addVpnAddress(String address, int prefixLength);
boolean removeVpnAddress(String address, int prefixLength);
+ boolean setUnderlyingNetworksForVpn(in Network[] networks);
}
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index c387055..384ab1c 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -21,12 +21,14 @@
import android.util.Pair;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.UnknownHostException;
import static android.system.OsConstants.IFA_F_DADFAILED;
import static android.system.OsConstants.IFA_F_DEPRECATED;
+import static android.system.OsConstants.IFA_F_OPTIMISTIC;
import static android.system.OsConstants.IFA_F_TENTATIVE;
import static android.system.OsConstants.RT_SCOPE_HOST;
import static android.system.OsConstants.RT_SCOPE_LINK;
@@ -93,6 +95,20 @@
}
/**
+ * Utility function to check if |address| is a Unique Local IPv6 Unicast Address
+ * (a.k.a. "ULA"; RFC 4193).
+ *
+ * Per RFC 4193 section 8, fc00::/7 identifies these addresses.
+ */
+ private boolean isIPv6ULA() {
+ if (address != null && address instanceof Inet6Address) {
+ byte[] bytes = address.getAddress();
+ return ((bytes[0] & (byte)0xfc) == (byte)0xfc);
+ }
+ return false;
+ }
+
+ /**
* Utility function for the constructors.
*/
private void init(InetAddress address, int prefixLength, int flags, int scope) {
@@ -268,8 +284,16 @@
* @hide
*/
public boolean isGlobalPreferred() {
+ /**
+ * Note that addresses flagged as IFA_F_OPTIMISTIC are
+ * simultaneously flagged as IFA_F_TENTATIVE (when the tentative
+ * state has cleared either DAD has succeeded or failed, and both
+ * flags are cleared regardless).
+ */
return (scope == RT_SCOPE_UNIVERSE &&
- (flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED | IFA_F_TENTATIVE)) == 0L);
+ !isIPv6ULA() &&
+ (flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED)) == 0L &&
+ ((flags & IFA_F_TENTATIVE) == 0L || (flags & IFA_F_OPTIMISTIC) != 0L));
}
/**
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 662c576..8b0dfc9 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -493,16 +493,16 @@
/**
* Removes a stacked link.
*
- * If there a stacked link with the same interfacename as link, it is
+ * If there is a stacked link with the given interface name, it is
* removed. Otherwise, nothing changes.
*
- * @param link The link to remove.
+ * @param iface The interface name of 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) {
- LinkProperties removed = mStackedLinks.remove(link.getInterfaceName());
+ public boolean removeStackedLink(String iface) {
+ if (iface != null) {
+ LinkProperties removed = mStackedLinks.remove(iface);
return removed != null;
}
return false;
@@ -675,17 +675,38 @@
}
/**
- * 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.
+ * Returns true if this link is provisioned for global IPv4 connectivity.
+ * This requires an IP address, default route, and DNS server.
+ *
+ * @return {@code true} if the link is provisioned, {@code false} otherwise.
+ */
+ private boolean hasIPv4() {
+ return (hasIPv4Address() &&
+ hasIPv4DefaultRoute() &&
+ hasIPv4DnsServer());
+ }
+
+ /**
+ * Returns true if this link is provisioned for global IPv6 connectivity.
+ * This requires an IP address, default route, and DNS server.
+ *
+ * @return {@code true} if the link is provisioned, {@code false} otherwise.
+ */
+ private boolean hasIPv6() {
+ return (hasGlobalIPv6Address() &&
+ hasIPv6DefaultRoute() &&
+ hasIPv6DnsServer());
+ }
+
+ /**
+ * Returns true if this link is provisioned for global connectivity,
+ * for at least one Internet Protocol family.
*
* @return {@code true} if the link is provisioned, {@code false} otherwise.
* @hide
*/
public boolean isProvisioned() {
- return (hasIPv4Address() ||
- (hasGlobalIPv6Address() && hasIPv6DefaultRoute() && hasIPv6DnsServer()));
+ return (hasIPv4() || hasIPv6());
}
/**
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 58f0fc0..5c12696 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -21,10 +21,13 @@
import android.os.Parcel;
import android.system.ErrnoException;
+import java.io.FileDescriptor;
import java.io.IOException;
+import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
+import java.net.ProxySelector;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
@@ -242,16 +245,46 @@
* @see java.net.URL#openConnection()
*/
public URLConnection openConnection(URL url) throws IOException {
+ final ConnectivityManager cm = ConnectivityManager.getInstance();
+ // TODO: Should this be optimized to avoid fetching the global proxy for every request?
+ ProxyInfo proxyInfo = cm.getGlobalProxy();
+ if (proxyInfo == null) {
+ // TODO: Should this be optimized to avoid fetching LinkProperties for every request?
+ final LinkProperties lp = cm.getLinkProperties(this);
+ if (lp != null) proxyInfo = lp.getHttpProxy();
+ }
+ java.net.Proxy proxy = null;
+ if (proxyInfo != null) {
+ proxy = proxyInfo.makeProxy();
+ } else {
+ proxy = java.net.Proxy.NO_PROXY;
+ }
+ return openConnection(url, proxy);
+ }
+
+ /**
+ * Opens the specified {@link URL} on this {@code Network}, such that all traffic will be sent
+ * on this Network. The URL protocol must be {@code HTTP} or {@code HTTPS}.
+ *
+ * @param proxy the proxy through which the connection will be established.
+ * @return a {@code URLConnection} to the resource referred to by this URL.
+ * @throws MalformedURLException if the URL protocol is not HTTP or HTTPS.
+ * @throws IllegalArgumentException if the argument proxy is null.
+ * @throws IOException if an error occurs while opening the connection.
+ * @see java.net.URL#openConnection()
+ * @hide
+ */
+ public URLConnection openConnection(URL url, java.net.Proxy proxy) throws IOException {
+ if (proxy == null) throw new IllegalArgumentException("proxy is null");
maybeInitHttpClient();
String protocol = url.getProtocol();
OkHttpClient client;
// TODO: HttpHandler creates OkHttpClients that share the default ResponseCache.
// Could this cause unexpected behavior?
- // TODO: Should the network's proxy be specified?
if (protocol.equals("http")) {
- client = HttpHandler.createHttpOkHttpClient(null /* proxy */);
+ client = HttpHandler.createHttpOkHttpClient(proxy);
} else if (protocol.equals("https")) {
- client = HttpsHandler.createHttpsOkHttpClient(null /* proxy */);
+ client = HttpsHandler.createHttpsOkHttpClient(proxy);
} else {
// OkHttpClient only supports HTTP and HTTPS and returns a null URLStreamHandler if
// passed another protocol.
@@ -264,18 +297,40 @@
}
/**
+ * Binds the specified {@link DatagramSocket} to this {@code Network}. All data traffic on the
+ * socket will be sent on this {@code Network}, irrespective of any process-wide network binding
+ * set by {@link ConnectivityManager#setProcessDefaultNetwork}. The socket must not be
+ * connected.
+ */
+ public void bindSocket(DatagramSocket socket) throws IOException {
+ // Apparently, the kernel doesn't update a connected UDP socket's routing upon mark changes.
+ if (socket.isConnected()) {
+ throw new SocketException("Socket is connected");
+ }
+ // Query a property of the underlying socket to ensure that the socket's file descriptor
+ // exists, is available to bind to a network and is not closed.
+ socket.getReuseAddress();
+ bindSocketFd(socket.getFileDescriptor$());
+ }
+
+ /**
* Binds the specified {@link Socket} to this {@code Network}. All data traffic on the socket
* will be sent on this {@code Network}, irrespective of any process-wide network binding set by
* {@link ConnectivityManager#setProcessDefaultNetwork}. The socket must not be connected.
*/
public void bindSocket(Socket socket) throws IOException {
+ // Apparently, the kernel doesn't update a connected TCP socket's routing upon mark changes.
if (socket.isConnected()) {
throw new SocketException("Socket is connected");
}
- // Query a property of the underlying socket to ensure the underlying
- // socket exists so a file descriptor is available to bind to a network.
+ // Query a property of the underlying socket to ensure that the socket's file descriptor
+ // exists, is available to bind to a network and is not closed.
socket.getReuseAddress();
- int err = NetworkUtils.bindSocketToNetwork(socket.getFileDescriptor$().getInt$(), netId);
+ bindSocketFd(socket.getFileDescriptor$());
+ }
+
+ private void bindSocketFd(FileDescriptor fd) throws IOException {
+ int err = NetworkUtils.bindSocketToNetwork(fd.getInt$(), netId);
if (err != 0) {
// bindSocketToNetwork returns negative errno.
throw new ErrnoException("Binding socket to network " + netId, -err)
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index f4e95c1..0720885 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -154,9 +154,16 @@
*/
public static final int NET_CAPABILITY_NOT_VPN = 15;
+ /**
+ * Indicates that connectivity on this network was successfully validated. For example, for a
+ * network with NET_CAPABILITY_INTERNET, it means that Internet connectivity was successfully
+ * detected.
+ * @hide
+ */
+ public static final int NET_CAPABILITY_VALIDATED = 16;
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VPN;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_VALIDATED;
/**
* Capabilities that are set by default when the object is constructed.
@@ -257,7 +264,8 @@
return ((nc.mNetworkCapabilities & this.mNetworkCapabilities) == this.mNetworkCapabilities);
}
- private boolean equalsNetCapabilities(NetworkCapabilities nc) {
+ /** @hide */
+ public boolean equalsNetCapabilities(NetworkCapabilities nc) {
return (nc.mNetworkCapabilities == this.mNetworkCapabilities);
}
@@ -386,7 +394,8 @@
return ((this.mTransportTypes == 0) ||
((this.mTransportTypes & nc.mTransportTypes) != 0));
}
- private boolean equalsTransportTypes(NetworkCapabilities nc) {
+ /** @hide */
+ public boolean equalsTransportTypes(NetworkCapabilities nc) {
return (nc.mTransportTypes == this.mTransportTypes);
}
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 5d2a43d..b92c9e3 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -20,15 +20,18 @@
import android.os.Parcelable;
/**
- * A grab-bag of information (metadata, policies, properties, etc) about a {@link Network}.
+ * A grab-bag of information (metadata, policies, properties, etc) about a
+ * {@link Network}. Since this contains PII, it should not be sent outside the
+ * system.
*
* @hide
*/
public class NetworkMisc implements Parcelable {
/**
- * If the {@link Network} is a VPN, whether apps are allowed to bypass the VPN. This is set by
- * a {@link VpnService} and used by {@link ConnectivityService} when creating a VPN.
+ * If the {@link Network} is a VPN, whether apps are allowed to bypass the
+ * VPN. This is set by a {@link VpnService} and used by
+ * {@link ConnectivityManager} when creating a VPN.
*/
public boolean allowBypass;
@@ -41,6 +44,11 @@
*/
public boolean explicitlySelected;
+ /**
+ * For mobile networks, this is the subscriber ID (such as IMSI).
+ */
+ public String subscriberId;
+
public NetworkMisc() {
}
@@ -48,6 +56,7 @@
if (nm != null) {
allowBypass = nm.allowBypass;
explicitlySelected = nm.explicitlySelected;
+ subscriberId = nm.subscriberId;
}
}
@@ -60,6 +69,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(allowBypass ? 1 : 0);
out.writeInt(explicitlySelected ? 1 : 0);
+ out.writeString(subscriberId);
}
public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
@@ -68,6 +78,7 @@
NetworkMisc networkMisc = new NetworkMisc();
networkMisc.allowBypass = in.readInt() != 0;
networkMisc.explicitlySelected = in.readInt() != 0;
+ networkMisc.subscriberId = in.readString();
return networkMisc;
}
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index 2e0e9e4..933287f 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -29,20 +29,17 @@
public final NetworkInfo networkInfo;
public final LinkProperties linkProperties;
public final NetworkCapabilities networkCapabilities;
- /** Currently only used by testing. */
+ public final Network network;
public final String subscriberId;
public final String networkId;
public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
- NetworkCapabilities networkCapabilities) {
- this(networkInfo, linkProperties, networkCapabilities, null, null);
- }
-
- public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
- NetworkCapabilities networkCapabilities, String subscriberId, String networkId) {
+ NetworkCapabilities networkCapabilities, Network network, String subscriberId,
+ String networkId) {
this.networkInfo = networkInfo;
this.linkProperties = linkProperties;
this.networkCapabilities = networkCapabilities;
+ this.network = network;
this.subscriberId = subscriberId;
this.networkId = networkId;
}
@@ -51,6 +48,7 @@
networkInfo = in.readParcelable(null);
linkProperties = in.readParcelable(null);
networkCapabilities = in.readParcelable(null);
+ network = in.readParcelable(null);
subscriberId = in.readString();
networkId = in.readString();
}
@@ -65,6 +63,7 @@
out.writeParcelable(networkInfo, flags);
out.writeParcelable(linkProperties, flags);
out.writeParcelable(networkCapabilities, flags);
+ out.writeParcelable(network, flags);
out.writeString(subscriberId);
out.writeString(networkId);
}
@@ -80,5 +79,4 @@
return new NetworkState[size];
}
};
-
}
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index 1534e2c..a3cad77 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -36,7 +36,13 @@
*
* 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}.
+ *
+ * @deprecated Please use {@link java.net.URL#openConnection}, {@link java.net.Proxy} and
+ * friends. The Apache HTTP client is no longer maintained and may be removed in a future
+ * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
+ * for further details.
*/
+@Deprecated
public class ProxyInfo implements Parcelable {
private String mHost;
@@ -254,7 +260,8 @@
if (!Uri.EMPTY.equals(mPacFileUrl)) {
sb.append("PAC Script: ");
sb.append(mPacFileUrl);
- } else if (mHost != null) {
+ }
+ if (mHost != null) {
sb.append("[");
sb.append(mHost);
sb.append("] ");
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 5a273cf..365f2b6 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -76,15 +76,22 @@
/**
* Returns the network routes specified by this object. Will typically include a
- * directly-connected route for the IP address's local subnet and a default route.
+ * directly-connected route for the IP address's local subnet and a default route. If the
+ * default gateway is not covered by the directly-connected route, it will also contain a host
+ * route to the gateway as well. This configuration is arguably invalid, but it used to work
+ * in K and earlier, and other OSes appear to accept it.
*/
public List<RouteInfo> getRoutes(String iface) {
- List<RouteInfo> routes = new ArrayList<RouteInfo>(2);
+ List<RouteInfo> routes = new ArrayList<RouteInfo>(3);
if (ipAddress != null) {
- routes.add(new RouteInfo(ipAddress, null, iface));
+ RouteInfo connectedRoute = new RouteInfo(ipAddress, null, iface);
+ routes.add(connectedRoute);
+ if (gateway != null && !connectedRoute.matches(gateway)) {
+ routes.add(RouteInfo.makeHostRoute(gateway, iface));
+ }
}
if (gateway != null) {
- routes.add(new RouteInfo((LinkAddress) null, gateway, iface));
+ routes.add(new RouteInfo((IpPrefix) null, gateway, iface));
}
return routes;
}
@@ -107,6 +114,7 @@
for (InetAddress dns : dnsServers) {
lp.addDnsServer(dns);
}
+ lp.setDomains(domains);
return lp;
}
diff --git a/core/tests/coretests/src/android/net/LinkAddressTest.java b/core/tests/coretests/src/android/net/LinkAddressTest.java
index 7bc3974..adf8d95 100644
--- a/core/tests/coretests/src/android/net/LinkAddressTest.java
+++ b/core/tests/coretests/src/android/net/LinkAddressTest.java
@@ -33,8 +33,11 @@
import static android.test.MoreAsserts.assertNotEqual;
import android.test.suitebuilder.annotation.SmallTest;
+import static android.system.OsConstants.IFA_F_DADFAILED;
import static android.system.OsConstants.IFA_F_DEPRECATED;
+import static android.system.OsConstants.IFA_F_OPTIMISTIC;
import static android.system.OsConstants.IFA_F_PERMANENT;
+import static android.system.OsConstants.IFA_F_TEMPORARY;
import static android.system.OsConstants.IFA_F_TENTATIVE;
import static android.system.OsConstants.RT_SCOPE_HOST;
import static android.system.OsConstants.RT_SCOPE_LINK;
@@ -340,4 +343,73 @@
l = new LinkAddress(V4 + "/28", IFA_F_PERMANENT, RT_SCOPE_LINK);
assertParcelingIsLossless(l);
}
+
+ private void assertGlobalPreferred(LinkAddress l, String msg) {
+ assertTrue(msg, l.isGlobalPreferred());
+ }
+
+ private void assertNotGlobalPreferred(LinkAddress l, String msg) {
+ assertFalse(msg, l.isGlobalPreferred());
+ }
+
+ public void testIsGlobalPreferred() {
+ LinkAddress l;
+
+ l = new LinkAddress(V4_ADDRESS, 32, 0, RT_SCOPE_UNIVERSE);
+ assertGlobalPreferred(l, "v4,global,noflags");
+
+ l = new LinkAddress("10.10.1.7/23", 0, RT_SCOPE_UNIVERSE);
+ assertGlobalPreferred(l, "v4-rfc1918,global,noflags");
+
+ l = new LinkAddress("10.10.1.7/23", 0, RT_SCOPE_SITE);
+ assertNotGlobalPreferred(l, "v4-rfc1918,site-local,noflags");
+
+ l = new LinkAddress("127.0.0.7/8", 0, RT_SCOPE_HOST);
+ assertNotGlobalPreferred(l, "v4-localhost,node-local,noflags");
+
+ l = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_UNIVERSE);
+ assertGlobalPreferred(l, "v6,global,noflags");
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_PERMANENT, RT_SCOPE_UNIVERSE);
+ assertGlobalPreferred(l, "v6,global,permanent");
+
+ // IPv6 ULAs are not acceptable "global preferred" addresses.
+ l = new LinkAddress("fc12::1/64", 0, RT_SCOPE_UNIVERSE);
+ assertNotGlobalPreferred(l, "v6,ula1,noflags");
+
+ l = new LinkAddress("fd34::1/64", 0, RT_SCOPE_UNIVERSE);
+ assertNotGlobalPreferred(l, "v6,ula2,noflags");
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_UNIVERSE);
+ assertGlobalPreferred(l, "v6,global,tempaddr");
+
+ l = new LinkAddress(V6_ADDRESS, 64, (IFA_F_TEMPORARY|IFA_F_DADFAILED),
+ RT_SCOPE_UNIVERSE);
+ assertNotGlobalPreferred(l, "v6,global,tempaddr+dadfailed");
+
+ l = new LinkAddress(V6_ADDRESS, 64, (IFA_F_TEMPORARY|IFA_F_DEPRECATED),
+ RT_SCOPE_UNIVERSE);
+ assertNotGlobalPreferred(l, "v6,global,tempaddr+deprecated");
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_SITE);
+ assertNotGlobalPreferred(l, "v6,site-local,tempaddr");
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_LINK);
+ assertNotGlobalPreferred(l, "v6,link-local,tempaddr");
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_HOST);
+ assertNotGlobalPreferred(l, "v6,node-local,tempaddr");
+
+ l = new LinkAddress("::1/128", IFA_F_PERMANENT, RT_SCOPE_HOST);
+ assertNotGlobalPreferred(l, "v6-localhost,node-local,permanent");
+
+ l = new LinkAddress(V6_ADDRESS, 64, (IFA_F_TEMPORARY|IFA_F_TENTATIVE),
+ RT_SCOPE_UNIVERSE);
+ assertNotGlobalPreferred(l, "v6,global,tempaddr+tentative");
+
+ l = new LinkAddress(V6_ADDRESS, 64,
+ (IFA_F_TEMPORARY|IFA_F_TENTATIVE|IFA_F_OPTIMISTIC),
+ RT_SCOPE_UNIVERSE);
+ assertGlobalPreferred(l, "v6,global,tempaddr+optimistic");
+ }
}
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index 4015b3d..abfed6e 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -31,8 +31,10 @@
"2001:0db8:85a3:0000:0000:8a2e:0370:7334");
private static InetAddress DNS1 = NetworkUtils.numericToInetAddress("75.208.7.1");
private static InetAddress DNS2 = NetworkUtils.numericToInetAddress("69.78.7.1");
+ private static InetAddress DNS6 = NetworkUtils.numericToInetAddress("2001:4860:4860::8888");
private static InetAddress GATEWAY1 = NetworkUtils.numericToInetAddress("75.208.8.1");
private static InetAddress GATEWAY2 = NetworkUtils.numericToInetAddress("69.78.8.1");
+ private static InetAddress GATEWAY6 = NetworkUtils.numericToInetAddress("fe80::6:0000:613");
private static String NAME = "qmi0";
private static int MTU = 1500;
@@ -338,14 +340,14 @@
assertFalse("newname".equals(link.getInterfaceName()));
}
- assertTrue(rmnet0.removeStackedLink(clat4));
+ assertTrue(rmnet0.removeStackedLink("clat4"));
assertEquals(0, rmnet0.getStackedLinks().size());
assertEquals(1, rmnet0.getAddresses().size());
assertEquals(1, rmnet0.getLinkAddresses().size());
assertEquals(1, rmnet0.getAllAddresses().size());
assertEquals(1, rmnet0.getAllLinkAddresses().size());
- assertFalse(rmnet0.removeStackedLink(clat4));
+ assertFalse(rmnet0.removeStackedLink("clat4"));
}
private LinkAddress getFirstLinkAddress(LinkProperties lp) {
@@ -370,7 +372,7 @@
assertTrue(stacked.hasGlobalIPv6Address());
assertFalse(lp.hasIPv4Address());
assertFalse(lp.hasGlobalIPv6Address());
- lp.removeStackedLink(stacked);
+ lp.removeStackedLink("stacked");
assertFalse(lp.hasIPv4Address());
assertFalse(lp.hasGlobalIPv6Address());
@@ -453,4 +455,47 @@
lp2.setLinkAddresses(lp.getLinkAddresses());
assertTrue(lp.equals(lp));
}
+
+ @SmallTest
+ public void testIsProvisioned() {
+ LinkProperties lp4 = new LinkProperties();
+ assertFalse("v4only:empty", lp4.isProvisioned());
+ lp4.addLinkAddress(LINKADDRV4);
+ assertFalse("v4only:addr-only", lp4.isProvisioned());
+ lp4.addDnsServer(DNS1);
+ assertFalse("v4only:addr+dns", lp4.isProvisioned());
+ lp4.addRoute(new RouteInfo(GATEWAY1));
+ assertTrue("v4only:addr+dns+route", lp4.isProvisioned());
+
+ LinkProperties lp6 = new LinkProperties();
+ assertFalse("v6only:empty", lp6.isProvisioned());
+ lp6.addLinkAddress(LINKADDRV6LINKLOCAL);
+ assertFalse("v6only:fe80-only", lp6.isProvisioned());
+ lp6.addDnsServer(DNS6);
+ assertFalse("v6only:fe80+dns", lp6.isProvisioned());
+ lp6.addRoute(new RouteInfo(GATEWAY6));
+ assertFalse("v6only:fe80+dns+route", lp6.isProvisioned());
+ lp6.addLinkAddress(LINKADDRV6);
+ assertTrue("v6only:fe80+global+dns+route", lp6.isProvisioned());
+ lp6.removeLinkAddress(LINKADDRV6LINKLOCAL);
+ assertTrue("v6only:global+dns+route", lp6.isProvisioned());
+
+ LinkProperties lp46 = new LinkProperties();
+ lp46.addLinkAddress(LINKADDRV4);
+ lp46.addLinkAddress(LINKADDRV6);
+ lp46.addDnsServer(DNS1);
+ lp46.addDnsServer(DNS6);
+ assertFalse("dualstack:missing-routes", lp46.isProvisioned());
+ lp46.addRoute(new RouteInfo(GATEWAY1));
+ assertTrue("dualstack:v4-provisioned", lp46.isProvisioned());
+ lp6.addRoute(new RouteInfo(GATEWAY6));
+ assertTrue("dualstack:both-provisioned", lp46.isProvisioned());
+
+ // A link with an IPv6 address and default route, but IPv4 DNS server.
+ LinkProperties mixed = new LinkProperties();
+ mixed.addLinkAddress(LINKADDRV6);
+ mixed.addDnsServer(DNS1);
+ mixed.addRoute(new RouteInfo(GATEWAY6));
+ assertFalse("mixed:addr6+route6+dns4", mixed.isProvisioned());
+ }
}
diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
index b181122..9a08f41 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
@@ -451,6 +451,40 @@
assertIndexBeforeAfter(stats, 4, 4, Long.MAX_VALUE);
}
+ public void testIntersects() throws Exception {
+ final long BUCKET_SIZE = HOUR_IN_MILLIS;
+ stats = new NetworkStatsHistory(BUCKET_SIZE);
+
+ final long FIRST_START = TEST_START;
+ final long FIRST_END = FIRST_START + (2 * HOUR_IN_MILLIS);
+ final long SECOND_START = TEST_START + WEEK_IN_MILLIS;
+ final long SECOND_END = SECOND_START + HOUR_IN_MILLIS;
+ final long THIRD_START = TEST_START + (2 * WEEK_IN_MILLIS);
+ final long THIRD_END = THIRD_START + (2 * HOUR_IN_MILLIS);
+
+ stats.recordData(FIRST_START, FIRST_END,
+ new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L));
+ stats.recordData(SECOND_START, SECOND_END,
+ new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L));
+ stats.recordData(THIRD_START, THIRD_END,
+ new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L));
+
+ assertFalse(stats.intersects(10, 20));
+ assertFalse(stats.intersects(TEST_START + YEAR_IN_MILLIS, TEST_START + YEAR_IN_MILLIS + 1));
+ assertFalse(stats.intersects(Long.MAX_VALUE, Long.MIN_VALUE));
+
+ assertTrue(stats.intersects(Long.MIN_VALUE, Long.MAX_VALUE));
+ assertTrue(stats.intersects(10, TEST_START + YEAR_IN_MILLIS));
+ assertTrue(stats.intersects(TEST_START, TEST_START));
+ assertTrue(stats.intersects(TEST_START + DAY_IN_MILLIS, TEST_START + DAY_IN_MILLIS + 1));
+ assertTrue(stats.intersects(TEST_START + DAY_IN_MILLIS, Long.MAX_VALUE));
+ assertTrue(stats.intersects(TEST_START + 1, Long.MAX_VALUE));
+
+ assertFalse(stats.intersects(Long.MIN_VALUE, TEST_START - 1));
+ assertTrue(stats.intersects(Long.MIN_VALUE, TEST_START));
+ assertTrue(stats.intersects(Long.MIN_VALUE, TEST_START + 1));
+ }
+
private static void assertIndexBeforeAfter(
NetworkStatsHistory stats, int before, int after, long time) {
assertEquals("unexpected before", before, stats.getIndexBefore(time));
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d7ff527..eab535a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -20,21 +20,8 @@
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
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_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;
+import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
@@ -45,11 +32,9 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
@@ -62,10 +47,8 @@
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
-import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.LinkProperties.CompareResult;
-import android.net.LinkQualityInfo;
import android.net.MobileDataStateTracker;
import android.net.Network;
import android.net.NetworkAgent;
@@ -73,7 +56,6 @@
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkFactory;
import android.net.NetworkMisc;
import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
@@ -81,16 +63,12 @@
import android.net.NetworkStateTracker;
import android.net.NetworkUtils;
import android.net.Proxy;
-import android.net.ProxyDataTracker;
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.SamplingDataTracker;
import android.net.UidRange;
import android.net.Uri;
-import android.net.wimax.WimaxManagerConstants;
-import android.os.AsyncTask;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.FileUtils;
import android.os.Handler;
@@ -104,7 +82,6 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -127,9 +104,6 @@
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
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;
@@ -147,8 +121,6 @@
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
-import dalvik.system.DexClassLoader;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -158,34 +130,25 @@
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.reflect.Constructor;
-import java.net.HttpURLConnection;
import java.net.Inet4Address;
-import java.net.Inet6Address;
import java.net.InetAddress;
-import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLSession;
-
/**
* @hide
*/
-public class ConnectivityService extends IConnectivityManager.Stub {
+public class ConnectivityService extends IConnectivityManager.Stub
+ implements PendingIntent.OnFinished {
private static final String TAG = "ConnectivityService";
private static final boolean DBG = true;
@@ -215,6 +178,10 @@
private static final int SAMPLE_INTERVAL_ELAPSED_REQUEST_CODE = 0;
+ // How long to delay to removal of a pending intent based request.
+ // See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
+ private final int mReleasePendingIntentDelayMs;
+
private PendingIntent mSampleIntervalElapsedIntent;
// Set network sampling interval at 12 minutes, this way, even if the timers get
@@ -239,8 +206,6 @@
private boolean mLockdownEnabled;
private LockdownVpnTracker mLockdownTracker;
- private Nat464Xlat mClat;
-
/** Lock around {@link #mUidRules} and {@link #mMeteredIfaces}. */
private Object mRulesLock = new Object();
/** Currently active network rules by UID. */
@@ -257,7 +222,6 @@
private Context mContext;
private int mNetworkPreference;
- private int mActiveDefaultNetwork = TYPE_NONE;
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
@@ -268,6 +232,7 @@
private static ConnectivityService sServiceInstance;
private INetworkManagementService mNetd;
+ private INetworkStatsService mStatsService;
private INetworkPolicyManager mPolicyManager;
private String mCurrentTcpBufferSizes;
@@ -275,6 +240,26 @@
private static final int ENABLED = 1;
private static final int DISABLED = 0;
+ // Arguments to rematchNetworkAndRequests()
+ private enum NascentState {
+ // Indicates a network was just validated for the first time. If the network is found to
+ // be unwanted (i.e. not satisfy any NetworkRequests) it is torn down.
+ JUST_VALIDATED,
+ // Indicates a network was not validated for the first time immediately prior to this call.
+ NOT_JUST_VALIDATED
+ };
+ private enum ReapUnvalidatedNetworks {
+ // Tear down unvalidated networks that have no chance (i.e. even if validated) of becoming
+ // the highest scoring network satisfying a NetworkRequest. This should be passed when it's
+ // known that there may be unvalidated networks that could potentially be reaped, and when
+ // all networks have been rematched against all NetworkRequests.
+ REAP,
+ // Don't reap unvalidated networks. This should be passed when it's known that there are
+ // no unvalidated networks that could potentially be reaped, and when some networks have
+ // not yet been rematched against all NetworkRequests.
+ DONT_REAP
+ };
+
/**
* used internally to change our mobile data enabled flag
*/
@@ -306,12 +291,6 @@
private static final int EVENT_SEND_STICKY_BROADCAST_INTENT = 11;
/**
- * Used internally to
- * {@link NetworkStateTracker#setPolicyDataEnable(boolean)}.
- */
- private static final int EVENT_SET_POLICY_DATA_ENABLE = 12;
-
- /**
* Used internally to disable fail fast of mobile data
*/
private static final int EVENT_ENABLE_FAIL_FAST_MOBILE_DATA = 14;
@@ -385,6 +364,19 @@
*/
private static final int EVENT_SYSTEM_READY = 25;
+ /**
+ * used to add a network request with a pending intent
+ * includes a NetworkRequestInfo
+ */
+ private static final int EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT = 26;
+
+ /**
+ * used to remove a pending intent and its associated network request.
+ * arg1 = UID of caller
+ * obj = PendingIntent
+ */
+ private static final int EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT = 27;
+
/** Handler used for internal events. */
final private InternalHandler mHandler;
@@ -398,6 +390,7 @@
private String mNetTransitionWakeLockCausedBy = "";
private int mNetTransitionWakeLockSerialNumber;
private int mNetTransitionWakeLockTimeout;
+ private final PowerManager.WakeLock mPendingIntentWakeLock;
private InetAddress mDefaultDns;
@@ -634,8 +627,12 @@
loge("Error setting defaultDns using " + dns);
}
+ mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
+
mContext = checkNotNull(context, "missing Context");
mNetd = checkNotNull(netManager, "missing INetworkManagementService");
+ mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
mKeyStore = KeyStore.getInstance();
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -652,6 +649,7 @@
mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
com.android.internal.R.integer.config_networkTransitionTimeout);
+ mPendingIntentWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mNetTrackers = new NetworkStateTracker[
ConnectivityManager.MAX_NETWORK_TYPE+1];
@@ -690,6 +688,15 @@
// ignore it - leave the entry null
}
}
+
+ // Forcibly add TYPE_VPN as a supported type, if it has not already been added via config.
+ if (mNetConfigs[TYPE_VPN] == null) {
+ // mNetConfigs is used only for "restore time", which isn't applicable to VPNs, so we
+ // don't need to add TYPE_VPN to mNetConfigs.
+ mLegacyTypeTracker.addSupportedType(TYPE_VPN);
+ mNetworksDefined++; // used only in the log() statement below.
+ }
+
if (VDBG) log("mNetworksDefined=" + mNetworksDefined);
mProtectedNetworks = new ArrayList<Integer>();
@@ -716,12 +723,10 @@
intentFilter.addAction(Intent.ACTION_USER_STOPPING);
mContext.registerReceiverAsUser(
mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
- mClat = new Nat464Xlat(mContext, mNetd, this, mTrackerHandler);
try {
mNetd.registerObserver(mTethering);
mNetd.registerObserver(mDataActivityObserver);
- mNetd.registerObserver(mClat);
} catch (RemoteException e) {
loge("Error registering observer :" + e);
}
@@ -778,17 +783,6 @@
throw new IllegalStateException("No free netIds");
}
- private int getConnectivityChangeDelay() {
- final ContentResolver cr = mContext.getContentResolver();
-
- /** Check system properties for the default value then use secure settings value, if any. */
- int defaultDelay = SystemProperties.getInt(
- "conn." + Settings.Global.CONNECTIVITY_CHANGE_DELAY,
- ConnectivityManager.CONNECTIVITY_CHANGE_DELAY_DEFAULT);
- return Settings.Global.getInt(cr, Settings.Global.CONNECTIVITY_CHANGE_DELAY,
- defaultDelay);
- }
-
private boolean teardown(NetworkStateTracker netTracker) {
if (netTracker.teardown()) {
netTracker.setTeardownRequested(true);
@@ -798,20 +792,94 @@
}
}
- /**
- * Check if UID should be blocked from using the network represented by the given networkType.
- * @deprecated Uses mLegacyTypeTracker; cannot deal with multiple Networks of the same type.
- */
- private boolean isNetworkBlocked(int networkType, int uid) {
- return isNetworkWithLinkPropertiesBlocked(getLinkPropertiesForType(networkType), uid);
+ private NetworkState getFilteredNetworkState(int networkType, int uid) {
+ NetworkInfo info = null;
+ LinkProperties lp = null;
+ NetworkCapabilities nc = null;
+ Network network = null;
+ String subscriberId = null;
+
+ if (mLegacyTypeTracker.isTypeSupported(networkType)) {
+ NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+ if (nai != null) {
+ synchronized (nai) {
+ info = new NetworkInfo(nai.networkInfo);
+ lp = new LinkProperties(nai.linkProperties);
+ nc = new NetworkCapabilities(nai.networkCapabilities);
+ network = new Network(nai.network);
+ subscriberId = (nai.networkMisc != null) ? nai.networkMisc.subscriberId : null;
+ }
+ info.setType(networkType);
+ } else {
+ info = new NetworkInfo(networkType, 0, getNetworkTypeName(networkType), "");
+ info.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
+ info.setIsAvailable(true);
+ lp = new LinkProperties();
+ nc = new NetworkCapabilities();
+ network = null;
+ }
+ info = getFilteredNetworkInfo(info, lp, uid);
+ }
+
+ return new NetworkState(info, lp, nc, network, subscriberId, null);
}
- /**
- * Check if UID should be blocked from using the network represented by the given
- * NetworkAgentInfo.
- */
- private boolean isNetworkBlocked(NetworkAgentInfo nai, int uid) {
- return isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid);
+ private NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) {
+ if (network == null) {
+ return null;
+ }
+ synchronized (mNetworkForNetId) {
+ return mNetworkForNetId.get(network.netId);
+ }
+ };
+
+ private Network[] getVpnUnderlyingNetworks(int uid) {
+ if (!mLockdownEnabled) {
+ int user = UserHandle.getUserId(uid);
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(user);
+ if (vpn != null && vpn.appliesToUid(uid)) {
+ return vpn.getUnderlyingNetworks();
+ }
+ }
+ }
+ return null;
+ }
+
+ private NetworkState getUnfilteredActiveNetworkState(int uid) {
+ NetworkInfo info = null;
+ LinkProperties lp = null;
+ NetworkCapabilities nc = null;
+ Network network = null;
+ String subscriberId = null;
+
+ NetworkAgentInfo nai = mNetworkForRequestId.get(mDefaultRequest.requestId);
+
+ final Network[] networks = getVpnUnderlyingNetworks(uid);
+ if (networks != null) {
+ // getUnderlyingNetworks() returns:
+ // null => there was no VPN, or the VPN didn't specify anything, so we use the default.
+ // empty array => the VPN explicitly said "no default network".
+ // non-empty array => the VPN specified one or more default networks; we use the
+ // first one.
+ if (networks.length > 0) {
+ nai = getNetworkAgentInfoForNetwork(networks[0]);
+ } else {
+ nai = null;
+ }
+ }
+
+ if (nai != null) {
+ synchronized (nai) {
+ info = new NetworkInfo(nai.networkInfo);
+ lp = new LinkProperties(nai.linkProperties);
+ nc = new NetworkCapabilities(nai.networkCapabilities);
+ network = new Network(nai.network);
+ subscriberId = (nai.networkMisc != null) ? nai.networkMisc.subscriberId : null;
+ }
+ }
+
+ return new NetworkState(info, lp, nc, network, subscriberId, null);
}
/**
@@ -838,40 +906,19 @@
/**
* Return a filtered {@link NetworkInfo}, potentially marked
* {@link DetailedState#BLOCKED} based on
- * {@link #isNetworkBlocked}.
- * @deprecated Uses mLegacyTypeTracker; cannot deal with multiple Networks of the same type.
+ * {@link #isNetworkWithLinkPropertiesBlocked}.
*/
- private NetworkInfo getFilteredNetworkInfo(int networkType, int uid) {
- NetworkInfo info = getNetworkInfoForType(networkType);
- return getFilteredNetworkInfo(info, networkType, uid);
- }
-
- /*
- * @deprecated Uses mLegacyTypeTracker; cannot deal with multiple Networks of the same type.
- */
- private NetworkInfo getFilteredNetworkInfo(NetworkInfo info, int networkType, int uid) {
- if (isNetworkBlocked(networkType, uid)) {
+ private NetworkInfo getFilteredNetworkInfo(NetworkInfo info, LinkProperties lp, int uid) {
+ if (info != null && isNetworkWithLinkPropertiesBlocked(lp, uid)) {
// network is blocked; clone and override state
info = new NetworkInfo(info);
info.setDetailedState(DetailedState.BLOCKED, null, null);
- if (VDBG) log("returning Blocked NetworkInfo");
+ if (DBG) {
+ log("returning Blocked NetworkInfo for ifname=" +
+ lp.getInterfaceName() + ", uid=" + uid);
+ }
}
- if (mLockdownTracker != null) {
- info = mLockdownTracker.augmentNetworkInfo(info);
- if (VDBG) log("returning Locked NetworkInfo");
- }
- return info;
- }
-
- private NetworkInfo getFilteredNetworkInfo(NetworkAgentInfo nai, int uid) {
- NetworkInfo info = nai.networkInfo;
- if (isNetworkBlocked(nai, uid)) {
- // network is blocked; clone and override state
- info = new NetworkInfo(info);
- info.setDetailedState(DetailedState.BLOCKED, null, null);
- if (DBG) log("returning Blocked NetworkInfo");
- }
- if (mLockdownTracker != null) {
+ if (info != null && mLockdownTracker != null) {
info = mLockdownTracker.augmentNetworkInfo(info);
if (DBG) log("returning Locked NetworkInfo");
}
@@ -889,7 +936,8 @@
public NetworkInfo getActiveNetworkInfo() {
enforceAccessPermission();
final int uid = Binder.getCallingUid();
- return getNetworkInfo(mActiveDefaultNetwork, uid);
+ NetworkState state = getUnfilteredActiveNetworkState(uid);
+ return getFilteredNetworkInfo(state.networkInfo, state.linkProperties, uid);
}
/**
@@ -924,8 +972,7 @@
NetworkInfo provNi = getProvisioningNetworkInfo();
if (provNi == null) {
- final int uid = Binder.getCallingUid();
- provNi = getNetworkInfo(mActiveDefaultNetwork, uid);
+ provNi = getActiveNetworkInfo();
}
if (DBG) log("getProvisioningOrActiveNetworkInfo: X provNi=" + provNi);
return provNi;
@@ -933,62 +980,59 @@
public NetworkInfo getActiveNetworkInfoUnfiltered() {
enforceAccessPermission();
- if (isNetworkTypeValid(mActiveDefaultNetwork)) {
- return getNetworkInfoForType(mActiveDefaultNetwork);
- }
- return null;
+ final int uid = Binder.getCallingUid();
+ NetworkState state = getUnfilteredActiveNetworkState(uid);
+ return state.networkInfo;
}
@Override
public NetworkInfo getActiveNetworkInfoForUid(int uid) {
enforceConnectivityInternalPermission();
- return getNetworkInfo(mActiveDefaultNetwork, uid);
+ NetworkState state = getUnfilteredActiveNetworkState(uid);
+ return getFilteredNetworkInfo(state.networkInfo, state.linkProperties, uid);
}
@Override
public NetworkInfo getNetworkInfo(int networkType) {
enforceAccessPermission();
final int uid = Binder.getCallingUid();
- return getNetworkInfo(networkType, uid);
+ if (getVpnUnderlyingNetworks(uid) != null) {
+ // A VPN is active, so we may need to return one of its underlying networks. This
+ // information is not available in LegacyTypeTracker, so we have to get it from
+ // getUnfilteredActiveNetworkState.
+ NetworkState state = getUnfilteredActiveNetworkState(uid);
+ if (state.networkInfo != null && state.networkInfo.getType() == networkType) {
+ return getFilteredNetworkInfo(state.networkInfo, state.linkProperties, uid);
+ }
+ }
+ NetworkState state = getFilteredNetworkState(networkType, uid);
+ return state.networkInfo;
}
- private NetworkInfo getNetworkInfo(int networkType, int uid) {
+ @Override
+ public NetworkInfo getNetworkInfoForNetwork(Network network) {
+ enforceAccessPermission();
+ final int uid = Binder.getCallingUid();
NetworkInfo info = null;
- if (isNetworkTypeValid(networkType)) {
- if (getNetworkInfoForType(networkType) != null) {
- info = getFilteredNetworkInfo(networkType, uid);
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ if (nai != null) {
+ synchronized (nai) {
+ info = new NetworkInfo(nai.networkInfo);
+ info = getFilteredNetworkInfo(info, nai.linkProperties, 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, uid);
- }
- }
-
- @Override
public NetworkInfo[] getAllNetworkInfo() {
enforceAccessPermission();
- final int uid = Binder.getCallingUid();
final ArrayList<NetworkInfo> result = Lists.newArrayList();
for (int networkType = 0; networkType <= ConnectivityManager.MAX_NETWORK_TYPE;
networkType++) {
- if (getNetworkInfoForType(networkType) != null) {
- result.add(getFilteredNetworkInfo(networkType, uid));
+ NetworkInfo info = getNetworkInfo(networkType);
+ if (info != null) {
+ result.add(info);
}
}
return result.toArray(new NetworkInfo[result.size()]);
@@ -998,11 +1042,11 @@
public Network getNetworkForType(int networkType) {
enforceAccessPermission();
final int uid = Binder.getCallingUid();
- if (isNetworkBlocked(networkType, uid)) {
- return null;
+ NetworkState state = getFilteredNetworkState(networkType, uid);
+ if (!isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid)) {
+ return state.network;
}
- NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
- return (nai == null) ? null : nai.network;
+ return null;
}
@Override
@@ -1017,10 +1061,76 @@
return result.toArray(new Network[result.size()]);
}
+ private NetworkCapabilities getNetworkCapabilitiesAndValidation(NetworkAgentInfo nai) {
+ if (nai != null) {
+ synchronized (nai) {
+ if (nai.created) {
+ NetworkCapabilities nc = new NetworkCapabilities(nai.networkCapabilities);
+ if (nai.lastValidated) {
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ } else {
+ nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ }
+ return nc;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
+ // The basic principle is: if an app's traffic could possibly go over a
+ // network, without the app doing anything multinetwork-specific,
+ // (hence, by "default"), then include that network's capabilities in
+ // the array.
+ //
+ // In the normal case, app traffic only goes over the system's default
+ // network connection, so that's the only network returned.
+ //
+ // With a VPN in force, some app traffic may go into the VPN, and thus
+ // over whatever underlying networks the VPN specifies, while other app
+ // traffic may go over the system default network (e.g.: a split-tunnel
+ // VPN, or an app disallowed by the VPN), so the set of networks
+ // returned includes the VPN's underlying networks and the system
+ // default.
+ enforceAccessPermission();
+
+ HashMap<Network, NetworkCapabilities> result = new HashMap<Network, NetworkCapabilities>();
+
+ NetworkAgentInfo nai = getDefaultNetwork();
+ NetworkCapabilities nc = getNetworkCapabilitiesAndValidation(getDefaultNetwork());
+ if (nc != null) {
+ result.put(nai.network, nc);
+ }
+
+ if (!mLockdownEnabled) {
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(userId);
+ if (vpn != null) {
+ Network[] networks = vpn.getUnderlyingNetworks();
+ if (networks != null) {
+ for (Network network : networks) {
+ nai = getNetworkAgentInfoForNetwork(network);
+ nc = getNetworkCapabilitiesAndValidation(nai);
+ if (nc != null) {
+ result.put(nai.network, nc);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ NetworkCapabilities[] out = new NetworkCapabilities[result.size()];
+ out = result.values().toArray(out);
+ return out;
+ }
+
@Override
public boolean isNetworkSupported(int networkType) {
enforceAccessPermission();
- return (isNetworkTypeValid(networkType) && (getNetworkInfoForType(networkType) != null));
+ return mLegacyTypeTracker.isTypeSupported(networkType);
}
/**
@@ -1033,14 +1143,20 @@
*/
@Override
public LinkProperties getActiveLinkProperties() {
- return getLinkPropertiesForType(mActiveDefaultNetwork);
+ enforceAccessPermission();
+ final int uid = Binder.getCallingUid();
+ NetworkState state = getUnfilteredActiveNetworkState(uid);
+ return state.linkProperties;
}
@Override
public LinkProperties getLinkPropertiesForType(int networkType) {
enforceAccessPermission();
- if (isNetworkTypeValid(networkType)) {
- return getLinkPropertiesForTypeInternal(networkType);
+ NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+ if (nai != null) {
+ synchronized (nai) {
+ return new LinkProperties(nai.linkProperties);
+ }
}
return null;
}
@@ -1049,11 +1165,7 @@
@Override
public LinkProperties getLinkProperties(Network network) {
enforceAccessPermission();
- NetworkAgentInfo nai = null;
- synchronized (mNetworkForNetId) {
- nai = mNetworkForNetId.get(network.netId);
- }
-
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai != null) {
synchronized (nai) {
return new LinkProperties(nai.linkProperties);
@@ -1065,10 +1177,7 @@
@Override
public NetworkCapabilities getNetworkCapabilities(Network network) {
enforceAccessPermission();
- NetworkAgentInfo nai = null;
- synchronized (mNetworkForNetId) {
- nai = mNetworkForNetId.get(network.netId);
- }
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai != null) {
synchronized (nai) {
return new NetworkCapabilities(nai.networkCapabilities);
@@ -1079,41 +1188,32 @@
@Override
public NetworkState[] getAllNetworkState() {
- enforceAccessPermission();
- final int uid = Binder.getCallingUid();
+ // Require internal since we're handing out IMSI details
+ enforceConnectivityInternalPermission();
+
final ArrayList<NetworkState> result = Lists.newArrayList();
- 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));
+ for (Network network : getAllNetworks()) {
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ if (nai != null) {
+ synchronized (nai) {
+ final String subscriberId = (nai.networkMisc != null)
+ ? nai.networkMisc.subscriberId : null;
+ result.add(new NetworkState(nai.networkInfo, nai.linkProperties,
+ nai.networkCapabilities, network, subscriberId, null));
+ }
}
}
return result.toArray(new NetworkState[result.size()]);
}
- private NetworkState getNetworkStateUnchecked(int networkType) {
- if (isNetworkTypeValid(networkType)) {
- NetworkInfo info = getNetworkInfoForType(networkType);
- if (info != null) {
- return new NetworkState(info,
- getLinkPropertiesForTypeInternal(networkType),
- getNetworkCapabilitiesForType(networkType));
- }
- }
- return null;
- }
-
@Override
public NetworkQuotaInfo getActiveNetworkQuotaInfo() {
enforceAccessPermission();
-
+ final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- final NetworkState state = getNetworkStateUnchecked(mActiveDefaultNetwork);
- if (state != null) {
+ final NetworkState state = getUnfilteredActiveNetworkState(uid);
+ if (state.networkInfo != null) {
try {
return mPolicyManager.getNetworkQuotaInfo(state);
} catch (RemoteException e) {
@@ -1128,17 +1228,18 @@
@Override
public boolean isActiveNetworkMetered() {
enforceAccessPermission();
+ final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- return isNetworkMeteredUnchecked(mActiveDefaultNetwork);
+ return isActiveNetworkMeteredUnchecked(uid);
} finally {
Binder.restoreCallingIdentity(token);
}
}
- private boolean isNetworkMeteredUnchecked(int networkType) {
- final NetworkState state = getNetworkStateUnchecked(networkType);
- if (state != null) {
+ private boolean isActiveNetworkMeteredUnchecked(int uid) {
+ final NetworkState state = getUnfilteredActiveNetworkState(uid);
+ if (state.networkInfo != null) {
try {
return mPolicyManager.isNetworkMetered(state);
} catch (RemoteException e) {
@@ -1309,38 +1410,21 @@
// kick off connectivity change broadcast for active network, since
// global background policy change is radical.
- final int networkType = mActiveDefaultNetwork;
- if (isNetworkTypeValid(networkType)) {
- final NetworkStateTracker tracker = mNetTrackers[networkType];
- if (tracker != null) {
- final NetworkInfo info = tracker.getNetworkInfo();
- if (info != null && info.isConnected()) {
- sendConnectedBroadcast(info);
- }
- }
- }
+ // TODO: Dead code; remove.
+ //
+ // final int networkType = mActiveDefaultNetwork;
+ // if (isNetworkTypeValid(networkType)) {
+ // final NetworkStateTracker tracker = mNetTrackers[networkType];
+ // if (tracker != null) {
+ // final NetworkInfo info = tracker.getNetworkInfo();
+ // if (info != null && info.isConnected()) {
+ // sendConnectedBroadcast(info);
+ // }
+ // }
+ // }
}
};
- @Override
- public void setPolicyDataEnable(int networkType, boolean enabled) {
- // only someone like NPMS should only be calling us
- mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
-
- mHandler.sendMessage(mHandler.obtainMessage(
- EVENT_SET_POLICY_DATA_ENABLE, networkType, (enabled ? ENABLED : DISABLED)));
- }
-
- private void handleSetPolicyDataEnable(int networkType, boolean enabled) {
- // TODO - handle this passing to factories
-// if (isNetworkTypeValid(networkType)) {
-// final NetworkStateTracker tracker = mNetTrackers[networkType];
-// if (tracker != null) {
-// tracker.setPolicyDataEnable(enabled);
-// }
-// }
- }
-
private void enforceInternetPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERNET,
@@ -1377,11 +1461,6 @@
sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
}
- private void sendConnectedBroadcastDelayed(NetworkInfo info, int delayMs) {
- sendGeneralBroadcast(info, CONNECTIVITY_ACTION_IMMEDIATE);
- sendGeneralBroadcastDelayed(info, CONNECTIVITY_ACTION, delayMs);
- }
-
private void sendInetConditionBroadcast(NetworkInfo info) {
sendGeneralBroadcast(info, ConnectivityManager.INET_CONDITION_ACTION);
}
@@ -1413,10 +1492,6 @@
sendStickyBroadcast(makeGeneralIntent(info, bcastType));
}
- private void sendGeneralBroadcastDelayed(NetworkInfo info, String bcastType, int delayMs) {
- sendStickyBroadcastDelayed(makeGeneralIntent(info, bcastType), delayMs);
- }
-
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);
@@ -1442,6 +1517,17 @@
}
final long ident = Binder.clearCallingIdentity();
+ if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
+ final IBatteryStats bs = BatteryStatsService.getService();
+ try {
+ NetworkInfo ni = intent.getParcelableExtra(
+ ConnectivityManager.EXTRA_NETWORK_INFO);
+ bs.noteConnectivityChanged(intent.getIntExtra(
+ ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_NONE),
+ ni != null ? ni.getState().toString() : "?");
+ } catch (RemoteException e) {
+ }
+ }
try {
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
} finally {
@@ -1450,19 +1536,6 @@
}
}
- private void sendStickyBroadcastDelayed(Intent intent, int delayMs) {
- if (delayMs <= 0) {
- sendStickyBroadcast(intent);
- } else {
- if (VDBG) {
- log("sendStickyBroadcastDelayed: delayMs=" + delayMs + ", action="
- + intent.getAction());
- }
- mHandler.sendMessageDelayed(mHandler.obtainMessage(
- EVENT_SEND_STICKY_BROADCAST_INTENT, intent), delayMs);
- }
- }
-
void systemReady() {
// start network sampling ..
Intent intent = new Intent(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED);
@@ -1745,16 +1818,6 @@
pw.println();
pw.decreaseIndent();
- pw.print("mActiveDefaultNetwork: " + mActiveDefaultNetwork);
- if (mActiveDefaultNetwork != TYPE_NONE) {
- NetworkInfo activeNetworkInfo = getActiveNetworkInfo();
- if (activeNetworkInfo != null) {
- pw.print(" " + activeNetworkInfo.getState() +
- "/" + activeNetworkInfo.getDetailedState());
- }
- }
- pw.println();
-
pw.println("mLegacyTypeTracker:");
pw.increaseIndent();
mLegacyTypeTracker.dump(pw);
@@ -1783,10 +1846,7 @@
private boolean isLiveNetworkAgent(NetworkAgentInfo nai, String msg) {
if (nai.network == null) return false;
- final NetworkAgentInfo officialNai;
- synchronized (mNetworkForNetId) {
- officialNai = mNetworkForNetId.get(nai.network.netId);
- }
+ final NetworkAgentInfo officialNai = getNetworkAgentInfoForNetwork(nai.network);
if (officialNai != null && officialNai.equals(nai)) return true;
if (officialNai != null || VDBG) {
loge(msg + " - isLiveNetworkAgent found mismatched netId: " + officialNai +
@@ -1912,18 +1972,18 @@
NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
if (isLiveNetworkAgent(nai, "EVENT_NETWORK_VALIDATED")) {
boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
+ nai.lastValidated = valid;
if (valid) {
if (DBG) log("Validated " + nai.name());
- final boolean previouslyValidated = nai.validated;
- final int previousScore = nai.getCurrentScore();
- nai.validated = true;
- rematchNetworkAndRequests(nai, !previouslyValidated);
- // If score has changed, rebroadcast to NetworkFactories. b/17726566
- if (nai.getCurrentScore() != previousScore) {
+ if (!nai.everValidated) {
+ nai.everValidated = true;
+ rematchNetworkAndRequests(nai, NascentState.JUST_VALIDATED,
+ ReapUnvalidatedNetworks.REAP);
+ // If score has changed, rebroadcast to NetworkFactories. b/17726566
sendUpdatedScoreToFactories(nai);
}
}
- updateInetCondition(nai, valid);
+ updateInetCondition(nai);
// Let the NetworkAgent know the state of its network
nai.asyncChannel.sendMessage(
android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS,
@@ -1982,7 +2042,7 @@
* to the link that may have incorrectly setup by the lower
* levels.
*/
- LinkProperties lp = getLinkPropertiesForTypeInternal(info.getType());
+ LinkProperties lp = getLinkPropertiesForType(info.getType());
if (DBG) {
log("EVENT_STATE_CHANGED: connected to provisioning network, lp=" + lp);
}
@@ -2020,6 +2080,20 @@
}
}
+ // Cancel any lingering so the linger timeout doesn't teardown a network.
+ // This should be called when a network begins satisfying a NetworkRequest.
+ // Note: depending on what state the NetworkMonitor is in (e.g.,
+ // if it's awaiting captive portal login, or if validation failed), this
+ // may trigger a re-evaluation of the network.
+ private void unlinger(NetworkAgentInfo nai) {
+ if (VDBG) log("Canceling linger of " + nai.name());
+ // If network has never been validated, it cannot have been lingered, so don't bother
+ // needlessly triggering a re-evaluation.
+ if (!nai.everValidated) return;
+ nai.networkLingered.clear();
+ nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
+ }
+
private void handleAsyncChannelHalfConnect(Message msg) {
AsyncChannel ac = (AsyncChannel) msg.obj;
if (mNetworkFactoryInfos.containsKey(msg.replyTo)) {
@@ -2055,6 +2129,7 @@
}
}
}
+
private void handleAsyncChannelDisconnected(Message msg) {
NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
if (nai != null) {
@@ -2082,6 +2157,7 @@
if (isDefaultNetwork(nai)) {
mDefaultInetConditionPublished = 0;
}
+ notifyIfacesChanged();
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
mNetworkAgentInfos.remove(msg.replyTo);
@@ -2104,11 +2180,8 @@
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) &&
+ for (NetworkAgentInfo existing : mNetworkAgentInfos.values()) {
+ if (existing.satisfies(request) &&
(alternative == null ||
alternative.getCurrentScore() < existing.getCurrentScore())) {
alternative = existing;
@@ -2124,63 +2197,83 @@
}
if (nai.networkRequests.get(mDefaultRequest.requestId) != null) {
removeDataActivityTracking(nai);
- mActiveDefaultNetwork = ConnectivityManager.TYPE_NONE;
notifyLockdownVpn(nai);
requestNetworkTransitionWakelock(nai.name());
}
for (NetworkAgentInfo networkToActivate : toActivate) {
- networkToActivate.networkLingered.clear();
- networkToActivate.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
- rematchNetworkAndRequests(networkToActivate, false);
+ unlinger(networkToActivate);
+ rematchNetworkAndRequests(networkToActivate, NascentState.NOT_JUST_VALIDATED,
+ ReapUnvalidatedNetworks.DONT_REAP);
}
}
}
+ // If this method proves to be too slow then we can maintain a separate
+ // pendingIntent => NetworkRequestInfo map.
+ // This method assumes that every non-null PendingIntent maps to exactly 1 NetworkRequestInfo.
+ private NetworkRequestInfo findExistingNetworkRequestInfo(PendingIntent pendingIntent) {
+ Intent intent = pendingIntent.getIntent();
+ for (Map.Entry<NetworkRequest, NetworkRequestInfo> entry : mNetworkRequests.entrySet()) {
+ PendingIntent existingPendingIntent = entry.getValue().mPendingIntent;
+ if (existingPendingIntent != null &&
+ existingPendingIntent.getIntent().filterEquals(intent)) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ private void handleRegisterNetworkRequestWithIntent(Message msg) {
+ final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
+
+ NetworkRequestInfo existingRequest = findExistingNetworkRequestInfo(nri.mPendingIntent);
+ if (existingRequest != null) { // remove the existing request.
+ if (DBG) log("Replacing " + existingRequest.request + " with "
+ + nri.request + " because their intents matched.");
+ handleReleaseNetworkRequest(existingRequest.request, getCallingUid());
+ }
+ handleRegisterNetworkRequest(msg);
+ }
+
private void handleRegisterNetworkRequest(Message msg) {
final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
- final NetworkCapabilities newCap = nri.request.networkCapabilities;
- int score = 0;
+
+ mNetworkRequests.put(nri.request, nri);
+
+ // TODO: This logic may be better replaced with a call to rematchNetworkAndRequests
// Check for the best currently alive network that satisfies this request
NetworkAgentInfo bestNetwork = null;
for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
if (DBG) log("handleRegisterNetworkRequest checking " + network.name());
- if (newCap.satisfiedByNetworkCapabilities(network.networkCapabilities)) {
+ if (network.satisfies(nri.request)) {
if (DBG) log("apparently satisfied. currentScore=" + network.getCurrentScore());
- if ((bestNetwork == null) ||
+ if (!nri.isRequest) {
+ // Not setting bestNetwork here as a listening NetworkRequest may be
+ // satisfied by multiple Networks. Instead the request is added to
+ // each satisfying Network and notified about each.
+ network.addRequest(nri.request);
+ notifyNetworkCallback(network, nri);
+ } else if (bestNetwork == null ||
bestNetwork.getCurrentScore() < network.getCurrentScore()) {
- if (!nri.isRequest) {
- // Not setting bestNetwork here as a listening NetworkRequest may be
- // satisfied by multiple Networks. Instead the request is added to
- // each satisfying Network and notified about each.
- network.addRequest(nri.request);
- notifyNetworkCallback(network, nri);
- } else {
- bestNetwork = network;
- }
+ bestNetwork = network;
}
}
}
if (bestNetwork != null) {
if (DBG) log("using " + bestNetwork.name());
- if (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);
- }
- // TODO: This logic may be better replaced with a call to rematchNetworkAndRequests
+ unlinger(bestNetwork);
bestNetwork.addRequest(nri.request);
mNetworkForRequestId.put(nri.request.requestId, bestNetwork);
notifyNetworkCallback(bestNetwork, nri);
- score = bestNetwork.getCurrentScore();
if (nri.request.legacyType != TYPE_NONE) {
mLegacyTypeTracker.add(nri.request.legacyType, bestNetwork);
}
}
- mNetworkRequests.put(nri.request, nri);
+
if (nri.isRequest) {
if (DBG) log("sending new NetworkRequest to factories");
+ final int score = bestNetwork == null ? 0 : bestNetwork.getCurrentScore();
for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
0, nri.request);
@@ -2188,6 +2281,54 @@
}
}
+ private void handleReleaseNetworkRequestWithIntent(PendingIntent pendingIntent,
+ int callingUid) {
+ NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
+ if (nri != null) {
+ handleReleaseNetworkRequest(nri.request, callingUid);
+ }
+ }
+
+ // Is nai unneeded by all NetworkRequests (and should be disconnected)?
+ // For validated Networks this is simply whether it is satsifying any NetworkRequests.
+ // For unvalidated Networks this is whether it is satsifying any NetworkRequests or
+ // were it to become validated, would it have a chance of satisfying any NetworkRequests.
+ private boolean unneeded(NetworkAgentInfo nai) {
+ if (!nai.created || nai.isVPN()) return false;
+ boolean unneeded = true;
+ if (nai.everValidated) {
+ for (int i = 0; i < nai.networkRequests.size() && unneeded; i++) {
+ final NetworkRequest nr = nai.networkRequests.valueAt(i);
+ try {
+ if (isRequest(nr)) unneeded = false;
+ } catch (Exception e) {
+ loge("Request " + nr + " not found in mNetworkRequests.");
+ loge(" it came from request list of " + nai.name());
+ }
+ }
+ } else {
+ for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+ // If this Network is already the highest scoring Network for a request, or if
+ // there is hope for it to become one if it validated, then it is needed.
+ if (nri.isRequest && nai.satisfies(nri.request) &&
+ (nai.networkRequests.get(nri.request.requestId) != null ||
+ // Note that this catches two important cases:
+ // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
+ // is currently satisfying the request. This is desirable when
+ // cellular ends up validating but WiFi does not.
+ // 2. Unvalidated WiFi will not be reaped when validated cellular
+ // is currently satsifying the request. This is desirable when
+ // WiFi ends up validating and out scoring cellular.
+ mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() <
+ nai.getCurrentScoreAsValidated())) {
+ unneeded = false;
+ break;
+ }
+ }
+ }
+ return unneeded;
+ }
+
private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) {
NetworkRequestInfo nri = mNetworkRequests.get(request);
if (nri != null) {
@@ -2201,6 +2342,9 @@
if (nri.isRequest) {
// Find all networks that are satisfying this request and remove the request
// from their request lists.
+ // TODO - it's my understanding that for a request there is only a single
+ // network satisfying it, so this loop is wasteful
+ boolean wasKept = false;
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
if (nai.networkRequests.get(nri.request.requestId) != null) {
nai.networkRequests.remove(nri.request.requestId);
@@ -2209,29 +2353,42 @@
", leaving " + nai.networkRequests.size() +
" requests.");
}
- // check if has any requests remaining and if not,
- // disconnect (unless it's a VPN).
- boolean keep = nai.isVPN();
- for (int i = 0; i < nai.networkRequests.size() && !keep; i++) {
- NetworkRequest r = nai.networkRequests.valueAt(i);
- if (isRequest(r)) keep = true;
- }
- if (!keep) {
+ if (unneeded(nai)) {
if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
- nai.asyncChannel.disconnect();
+ teardownUnneededNetwork(nai);
+ } else {
+ // suspect there should only be one pass through here
+ // but if any were kept do the check below
+ wasKept |= true;
}
}
}
- // Maintain the illusion. When this request arrived, we might have preteneded
- // that a network connected to serve it, even though the network was already
- // connected. Now that this request has gone away, we might have to pretend
- // that the network disconnected. LegacyTypeTracker will generate that
- // phatom disconnect for this type.
NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
if (nai != null) {
mNetworkForRequestId.remove(nri.request.requestId);
- if (nri.request.legacyType != TYPE_NONE) {
+ }
+ // Maintain the illusion. When this request arrived, we might have pretended
+ // that a network connected to serve it, even though the network was already
+ // connected. Now that this request has gone away, we might have to pretend
+ // that the network disconnected. LegacyTypeTracker will generate that
+ // phantom disconnect for this type.
+ if (nri.request.legacyType != TYPE_NONE && nai != null) {
+ boolean doRemove = true;
+ if (wasKept) {
+ // check if any of the remaining requests for this network are for the
+ // same legacy type - if so, don't remove the nai
+ for (int i = 0; i < nai.networkRequests.size(); i++) {
+ NetworkRequest otherRequest = nai.networkRequests.valueAt(i);
+ if (otherRequest.legacyType == nri.request.legacyType &&
+ isRequest(otherRequest)) {
+ if (DBG) log(" still have other legacy request - leaving");
+ doRemove = false;
+ }
+ }
+ }
+
+ if (doRemove) {
mLegacyTypeTracker.remove(nri.request.legacyType, nai);
}
}
@@ -2258,7 +2415,6 @@
@Override
public void handleMessage(Message msg) {
- NetworkInfo info;
switch (msg.what) {
case EVENT_EXPIRE_NET_TRANSITION_WAKELOCK:
case EVENT_CLEAR_NET_TRANSITION_WAKELOCK: {
@@ -2294,12 +2450,6 @@
sendStickyBroadcast(intent);
break;
}
- case EVENT_SET_POLICY_DATA_ENABLE: {
- final int networkType = msg.arg1;
- final boolean enabled = msg.arg2 == ENABLED;
- handleSetPolicyDataEnable(networkType, enabled);
- break;
- }
case EVENT_ENABLE_FAIL_FAST_MOBILE_DATA: {
int tag = mEnableFailFastMobileDataTag.get();
if (msg.arg1 == tag) {
@@ -2339,6 +2489,14 @@
handleRegisterNetworkRequest(msg);
break;
}
+ case EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT: {
+ handleRegisterNetworkRequestWithIntent(msg);
+ break;
+ }
+ case EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT: {
+ handleReleaseNetworkRequestWithIntent((PendingIntent) msg.obj, msg.arg1);
+ break;
+ }
case EVENT_RELEASE_NETWORK_REQUEST: {
handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.arg1);
break;
@@ -2479,9 +2637,15 @@
// 100 percent is full good, 0 is full bad.
public void reportInetCondition(int networkType, int percentage) {
- if (percentage > 50) return; // don't handle good network reports
NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
- if (nai != null) reportBadNetwork(nai.network);
+ if (nai == null) return;
+ boolean isGood = percentage > 50;
+ // Revalidate if the app report does not match our current validated state.
+ if (isGood != nai.lastValidated) {
+ // Make the message logged by reportBadNetwork below less confusing.
+ if (DBG && isGood) log("reportInetCondition: type=" + networkType + " ok, revalidate");
+ reportBadNetwork(nai.network);
+ }
}
public void reportBadNetwork(Network network) {
@@ -2491,10 +2655,7 @@
if (network == null) return;
final int uid = Binder.getCallingUid();
- NetworkAgentInfo nai = null;
- synchronized (mNetworkForNetId) {
- nai = mNetworkForNetId.get(network.netId);
- }
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai == null) return;
if (DBG) log("reportBadNetwork(" + nai.name() + ") by " + uid);
synchronized (nai) {
@@ -2502,13 +2663,13 @@
// which isn't meant to work on uncreated networks.
if (!nai.created) return;
- if (isNetworkBlocked(nai, uid)) return;
+ if (isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid)) return;
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_FORCE_REEVALUATION, uid);
}
}
- public ProxyInfo getProxy() {
+ public ProxyInfo getDefaultProxy() {
// 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.
@@ -2520,6 +2681,34 @@
}
}
+ // Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
+ // (e.g. if mGlobalProxy==null fall back to network-specific proxy, if network-specific
+ // proxy is null then there is no proxy in place).
+ private ProxyInfo canonicalizeProxyInfo(ProxyInfo proxy) {
+ if (proxy != null && TextUtils.isEmpty(proxy.getHost())
+ && (proxy.getPacFileUrl() == null || Uri.EMPTY.equals(proxy.getPacFileUrl()))) {
+ proxy = null;
+ }
+ return proxy;
+ }
+
+ // ProxyInfo equality function with a couple modifications over ProxyInfo.equals() to make it
+ // better for determining if a new proxy broadcast is necessary:
+ // 1. Canonicalize empty ProxyInfos to null so an empty proxy compares equal to null so as to
+ // avoid unnecessary broadcasts.
+ // 2. Make sure all parts of the ProxyInfo's compare true, including the host when a PAC URL
+ // is in place. This is important so legacy PAC resolver (see com.android.proxyhandler)
+ // changes aren't missed. The legacy PAC resolver pretends to be a simple HTTP proxy but
+ // actually uses the PAC to resolve; this results in ProxyInfo's with PAC URL, host and port
+ // all set.
+ private boolean proxyInfoEqual(ProxyInfo a, ProxyInfo b) {
+ a = canonicalizeProxyInfo(a);
+ b = canonicalizeProxyInfo(b);
+ // ProxyInfo.equals() doesn't check hosts when PAC URLs are present, but we need to check
+ // hosts even when PAC URLs are present to account for the legacy PAC resolver.
+ return Objects.equals(a, b) && (a == null || Objects.equals(a.getHost(), b.getHost()));
+ }
+
public void setGlobalProxy(ProxyInfo proxyProperties) {
enforceConnectivityInternalPermission();
@@ -2637,6 +2826,20 @@
}
}
+ // If the proxy has changed from oldLp to newLp, resend proxy broadcast with default proxy.
+ // This method gets called when any network changes proxy, but the broadcast only ever contains
+ // the default proxy (even if it hasn't changed).
+ // TODO: Deprecate the broadcast extras as they aren't necessarily applicable in a multi-network
+ // world where an app might be bound to a non-default network.
+ private void updateProxy(LinkProperties newLp, LinkProperties oldLp, NetworkAgentInfo nai) {
+ ProxyInfo newProxyInfo = newLp == null ? null : newLp.getHttpProxy();
+ ProxyInfo oldProxyInfo = oldLp == null ? null : oldLp.getHttpProxy();
+
+ if (!proxyInfoEqual(newProxyInfo, oldProxyInfo)) {
+ sendProxyBroadcast(getDefaultProxy());
+ }
+ }
+
private void handleDeprecatedGlobalHttpProxy() {
String proxy = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.HTTP_PROXY);
@@ -2705,42 +2908,6 @@
Slog.e(TAG, s);
}
- int convertFeatureToNetworkType(int networkType, String feature) {
- int usedNetworkType = networkType;
-
- if(networkType == ConnectivityManager.TYPE_MOBILE) {
- if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
- usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
- } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
- usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
- } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN) ||
- TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN_ALWAYS)) {
- usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
- } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
- usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
- } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_FOTA)) {
- usedNetworkType = ConnectivityManager.TYPE_MOBILE_FOTA;
- } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_IMS)) {
- 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!");
- }
- } else if (networkType == ConnectivityManager.TYPE_WIFI) {
- if (TextUtils.equals(feature, "p2p")) {
- usedNetworkType = ConnectivityManager.TYPE_WIFI_P2P;
- } else {
- Slog.e(TAG, "Can't match any wifi netTracker!");
- }
- } else {
- Slog.e(TAG, "Unexpected network type");
- }
- return usedNetworkType;
- }
-
private static <T> T checkNotNull(T value, String message) {
if (value == null) {
throw new NullPointerException(message);
@@ -2749,9 +2916,8 @@
}
/**
- * Prepare for a VPN application. This method is used by VpnDialogs
- * and not available in ConnectivityManager. Permissions are checked
- * in Vpn class.
+ * Prepare for a VPN application.
+ * Permissions are checked in Vpn class.
* @hide
*/
@Override
@@ -2765,8 +2931,8 @@
/**
* Set whether the current VPN package has the ability to launch VPNs without
- * user intervention. This method is used by system UIs and not available
- * in ConnectivityManager. Permissions are checked in Vpn class.
+ * user intervention. This method is used by system-privileged apps.
+ * Permissions are checked in Vpn class.
* @hide
*/
@Override
@@ -3183,7 +3349,7 @@
Settings.Global.putInt(cr, Settings.Global.AIRPLANE_MODE_ON, enable ? 1 : 0);
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra("state", enable);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3227,43 +3393,6 @@
}
};
- @Override
- public LinkQualityInfo getLinkQualityInfo(int networkType) {
- enforceAccessPermission();
- if (isNetworkTypeValid(networkType) && mNetTrackers[networkType] != null) {
- return mNetTrackers[networkType].getLinkQualityInfo();
- } else {
- return null;
- }
- }
-
- @Override
- public LinkQualityInfo getActiveLinkQualityInfo() {
- enforceAccessPermission();
- if (isNetworkTypeValid(mActiveDefaultNetwork) &&
- mNetTrackers[mActiveDefaultNetwork] != null) {
- return mNetTrackers[mActiveDefaultNetwork].getLinkQualityInfo();
- } else {
- return null;
- }
- }
-
- @Override
- public LinkQualityInfo[] getAllLinkQualityInfo() {
- enforceAccessPermission();
- final ArrayList<LinkQualityInfo> result = Lists.newArrayList();
- for (NetworkStateTracker tracker : mNetTrackers) {
- if (tracker != null) {
- LinkQualityInfo li = tracker.getLinkQualityInfo();
- if (li != null) {
- result.add(li);
- }
- }
- }
-
- return result.toArray(new LinkQualityInfo[result.size()]);
- }
-
/* Infrastructure for network sampling */
private void handleNetworkSamplingTimeout() {
@@ -3353,12 +3482,24 @@
static final boolean LISTEN = false;
final NetworkRequest request;
- IBinder mBinder;
+ final PendingIntent mPendingIntent;
+ boolean mPendingIntentSent;
+ private final IBinder mBinder;
final int mPid;
final int mUid;
final Messenger messenger;
final boolean isRequest;
+ NetworkRequestInfo(NetworkRequest r, PendingIntent pi, boolean isRequest) {
+ request = r;
+ mPendingIntent = pi;
+ messenger = null;
+ mBinder = null;
+ mPid = getCallingPid();
+ mUid = getCallingUid();
+ this.isRequest = isRequest;
+ }
+
NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, boolean isRequest) {
super();
messenger = m;
@@ -3367,6 +3508,7 @@
mPid = getCallingPid();
mUid = getCallingUid();
this.isRequest = isRequest;
+ mPendingIntent = null;
try {
mBinder.linkToDeath(this, 0);
@@ -3376,7 +3518,9 @@
}
void unlinkDeathRecipient() {
- mBinder.unlinkToDeath(this, 0);
+ if (mBinder != null) {
+ mBinder.unlinkToDeath(this, 0);
+ }
}
public void binderDied() {
@@ -3387,40 +3531,22 @@
public String toString() {
return (isRequest ? "Request" : "Listen") + " from uid/pid:" + mUid + "/" +
- mPid + " for " + request;
+ mPid + " for " + request +
+ (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
}
}
@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();
- }
-
networkCapabilities = new NetworkCapabilities(networkCapabilities);
-
- // if UID is restricted, don't allow them to bring up metered APNs
- if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
- == false) {
- final int uidRules;
- final int uid = Binder.getCallingUid();
- synchronized(mRulesLock) {
- uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
- }
- if ((uidRules & RULE_REJECT_METERED) != 0) {
- // we could silently fail or we can filter the available nets to only give
- // them those they have access to. Chose the more useful
- networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- }
- }
+ enforceNetworkRequestPermissions(networkCapabilities);
+ enforceMeteredApnPolicy(networkCapabilities);
if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) {
throw new IllegalArgumentException("Bad timeout specified");
}
+
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
nextNetworkRequestId());
if (DBG) log("requestNetwork for " + networkRequest);
@@ -3435,11 +3561,60 @@
return networkRequest;
}
+ private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities) {
+ if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ == false) {
+ enforceConnectivityInternalPermission();
+ } else {
+ enforceChangePermission();
+ }
+ }
+
+ private void enforceMeteredApnPolicy(NetworkCapabilities networkCapabilities) {
+ // if UID is restricted, don't allow them to bring up metered APNs
+ if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ == false) {
+ final int uidRules;
+ final int uid = Binder.getCallingUid();
+ synchronized(mRulesLock) {
+ uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
+ }
+ if ((uidRules & RULE_REJECT_METERED) != 0) {
+ // we could silently fail or we can filter the available nets to only give
+ // them those they have access to. Chose the more useful
+ networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ }
+ }
+ }
+
@Override
public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities,
PendingIntent operation) {
- // TODO
- return null;
+ checkNotNull(operation, "PendingIntent cannot be null.");
+ networkCapabilities = new NetworkCapabilities(networkCapabilities);
+ enforceNetworkRequestPermissions(networkCapabilities);
+ enforceMeteredApnPolicy(networkCapabilities);
+
+ NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
+ nextNetworkRequestId());
+ if (DBG) log("pendingRequest for " + networkRequest + " to trigger " + operation);
+ NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation,
+ NetworkRequestInfo.REQUEST);
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT,
+ nri));
+ return networkRequest;
+ }
+
+ private void releasePendingNetworkRequestWithDelay(PendingIntent operation) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT,
+ getCallingUid(), 0, operation), mReleasePendingIntentDelayMs);
+ }
+
+ @Override
+ public void releasePendingNetworkRequest(PendingIntent operation) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT,
+ getCallingUid(), 0, operation));
}
@Override
@@ -3513,10 +3688,15 @@
private final HashMap<Messenger, NetworkAgentInfo> mNetworkAgentInfos =
new HashMap<Messenger, NetworkAgentInfo>();
+ // Note: if mDefaultRequest is changed, NetworkMonitor needs to be updated.
private final NetworkRequest mDefaultRequest;
+ private NetworkAgentInfo getDefaultNetwork() {
+ return mNetworkForRequestId.get(mDefaultRequest.requestId);
+ }
+
private boolean isDefaultNetwork(NetworkAgentInfo nai) {
- return mNetworkForRequestId.get(mDefaultRequest.requestId) == nai;
+ return nai == getDefaultNetwork();
}
public void registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
@@ -3524,10 +3704,12 @@
int currentScore, NetworkMisc networkMisc) {
enforceConnectivityInternalPermission();
+ // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
+ // satisfies mDefaultRequest.
NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
new NetworkInfo(networkInfo), new LinkProperties(linkProperties),
new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,
- new NetworkMisc(networkMisc));
+ new NetworkMisc(networkMisc), mDefaultRequest);
synchronized (this) {
nai.networkMonitor.systemReady = mSystemReady;
}
@@ -3551,7 +3733,9 @@
// The NetworkAgentInfo does not know whether clatd is running on its network or not. Before
// we do anything else, make sure its LinkProperties are accurate.
- mClat.fixupLinkProperties(networkAgent, oldLp);
+ if (networkAgent.clatd != null) {
+ networkAgent.clatd.fixupLinkProperties(oldLp);
+ }
updateInterfaces(newLp, oldLp, netId);
updateMtu(newLp, oldLp);
@@ -3560,25 +3744,36 @@
// updateMtu(lp, null);
// }
updateTcpBufferSizes(networkAgent);
+
+ // TODO: deprecate and remove mDefaultDns when we can do so safely.
+ // For now, use it only when the network has Internet access. http://b/18327075
+ final boolean useDefaultDns = networkAgent.networkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_INTERNET);
final boolean flushDns = updateRoutes(newLp, oldLp, netId);
- updateDnses(newLp, oldLp, netId, flushDns);
+ updateDnses(newLp, oldLp, netId, flushDns, useDefaultDns);
+
updateClat(newLp, oldLp, networkAgent);
- if (isDefaultNetwork(networkAgent)) handleApplyDefaultProxy(newLp.getHttpProxy());
+ if (isDefaultNetwork(networkAgent)) {
+ handleApplyDefaultProxy(newLp.getHttpProxy());
+ } else {
+ updateProxy(newLp, oldLp, networkAgent);
+ }
// TODO - move this check to cover the whole function
if (!Objects.equals(newLp, oldLp)) {
+ notifyIfacesChanged();
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
}
}
- private void updateClat(LinkProperties newLp, LinkProperties oldLp, NetworkAgentInfo na) {
- final boolean wasRunningClat = mClat.isRunningClat(na);
- final boolean shouldRunClat = Nat464Xlat.requiresClat(na);
+ private void updateClat(LinkProperties newLp, LinkProperties oldLp, NetworkAgentInfo nai) {
+ final boolean wasRunningClat = nai.clatd != null && nai.clatd.isStarted();
+ final boolean shouldRunClat = Nat464Xlat.requiresClat(nai);
if (!wasRunningClat && shouldRunClat) {
- // Start clatd. If it's already been started but is not running yet, this is a no-op.
- mClat.startClat(na);
+ nai.clatd = new Nat464Xlat(mContext, mNetd, mTrackerHandler, nai);
+ nai.clatd.start();
} else if (wasRunningClat && !shouldRunClat) {
- mClat.stopClat();
+ nai.clatd.stop();
}
}
@@ -3655,10 +3850,11 @@
}
return !routeDiff.added.isEmpty() || !routeDiff.removed.isEmpty();
}
- private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId, boolean flush) {
+ private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId,
+ boolean flush, boolean useDefaultDns) {
if (oldLp == null || (newLp.isIdenticalDnses(oldLp) == false)) {
Collection<InetAddress> dnses = newLp.getDnsServers();
- if (dnses.size() == 0 && mDefaultDns != null) {
+ if (dnses.size() == 0 && mDefaultDns != null && useDefaultDns) {
dnses = new ArrayList();
dnses.add(mDefaultDns);
if (DBG) {
@@ -3702,11 +3898,8 @@
mNumDnsEntries = last;
}
-
private void updateCapabilities(NetworkAgentInfo networkAgent,
NetworkCapabilities networkCapabilities) {
- // TODO - turn this on in MR1 when we have more dogfooding time.
- // rematchAllNetworksAndRequests();
if (!Objects.equals(networkAgent.networkCapabilities, networkCapabilities)) {
if (networkAgent.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) !=
networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
@@ -3721,6 +3914,7 @@
synchronized (networkAgent) {
networkAgent.networkCapabilities = networkCapabilities;
}
+ rematchAllNetworksAndRequests(networkAgent, networkAgent.getCurrentScore());
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
}
@@ -3742,6 +3936,41 @@
}
}
+ private void sendPendingIntentForRequest(NetworkRequestInfo nri, NetworkAgentInfo networkAgent,
+ int notificationType) {
+ if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE && !nri.mPendingIntentSent) {
+ Intent intent = new Intent();
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK, networkAgent.network);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.request);
+ nri.mPendingIntentSent = true;
+ sendIntent(nri.mPendingIntent, intent);
+ }
+ // else not handled
+ }
+
+ private void sendIntent(PendingIntent pendingIntent, Intent intent) {
+ mPendingIntentWakeLock.acquire();
+ try {
+ if (DBG) log("Sending " + pendingIntent);
+ pendingIntent.send(mContext, 0, intent, this /* onFinished */, null /* Handler */);
+ } catch (PendingIntent.CanceledException e) {
+ if (DBG) log(pendingIntent + " was not sent, it had been canceled.");
+ mPendingIntentWakeLock.release();
+ releasePendingNetworkRequest(pendingIntent);
+ }
+ // ...otherwise, mPendingIntentWakeLock.release() gets called by onSendFinished()
+ }
+
+ @Override
+ public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
+ String resultData, Bundle resultExtras) {
+ if (DBG) log("Finished sending " + pendingIntent);
+ mPendingIntentWakeLock.release();
+ // Release with a delay so the receiving client has an opportunity to put in its
+ // own request.
+ releasePendingNetworkRequestWithDelay(pendingIntent);
+ }
+
private void callCallbackForRequest(NetworkRequestInfo nri,
NetworkAgentInfo networkAgent, int notificationType) {
if (nri.messenger == null) return; // Default request has no msgr
@@ -3805,7 +4034,6 @@
private void makeDefault(NetworkAgentInfo newNetwork) {
if (DBG) log("Switching to new default network: " + newNetwork);
- mActiveDefaultNetwork = newNetwork.networkInfo.getType();
setupDataActivityTracking(newNetwork);
try {
mNetd.setDefaultNetId(newNetwork.network.netId);
@@ -3815,6 +4043,7 @@
notifyLockdownVpn(newNetwork);
handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
updateTcpBufferSizes(newNetwork);
+ setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
}
// Handles a network appearing or improving its score.
@@ -3823,15 +4052,16 @@
// satisfied by newNetwork, and reassigns to newNetwork
// any such requests for which newNetwork is the best.
//
- // - Lingers any Networks that as a result are no longer
+ // - Lingers any validated Networks that as a result are no longer
// needed. A network is needed if it is the best network for
// one or more NetworkRequests, or if it is a VPN.
//
// - Tears down newNetwork if it just became validated
- // (i.e. nascent==true) but turns out to be unneeded.
- // Does not tear down newNetwork if it is unvalidated,
- // because future validation may improve newNetwork's
- // score enough that it is needed.
+ // (i.e. nascent==JUST_VALIDATED) but turns out to be unneeded.
+ //
+ // - If reapUnvalidatedNetworks==REAP, tears down unvalidated
+ // networks that have no chance (i.e. even if validated)
+ // of becoming the highest scoring network.
//
// NOTE: This function only adds NetworkRequests that "newNetwork" could satisfy,
// it does not remove NetworkRequests that other Networks could better satisfy.
@@ -3839,18 +4069,21 @@
// This function should be used when possible instead of {@code rematchAllNetworksAndRequests}
// as it performs better by a factor of the number of Networks.
//
+ // @param newNetwork is the network to be matched against NetworkRequests.
// @param nascent indicates if newNetwork just became validated, in which case it should be
- // torn down if unneeded. If nascent is false, no action is taken if newNetwork
- // is found to be unneeded by this call. Presumably, in this case, either:
- // - newNetwork is unvalidated (and left alive), or
- // - the NetworkRequests keeping newNetwork alive have been transitioned to
- // another higher scoring network by another call to rematchNetworkAndRequests()
- // and this other call also lingered newNetwork.
- private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork, boolean nascent) {
- if (!newNetwork.created) loge("ERROR: uncreated network being rematched.");
- if (nascent && !newNetwork.validated) loge("ERROR: nascent network not validated.");
+ // torn down if unneeded.
+ // @param reapUnvalidatedNetworks indicates if an additional pass over all networks should be
+ // performed to tear down unvalidated networks that have no chance (i.e. even if
+ // validated) of becoming the highest scoring network.
+ private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork, NascentState nascent,
+ ReapUnvalidatedNetworks reapUnvalidatedNetworks) {
+ if (!newNetwork.created) return;
+ if (nascent == NascentState.JUST_VALIDATED && !newNetwork.everValidated) {
+ loge("ERROR: nascent network not validated.");
+ }
boolean keep = newNetwork.isVPN();
boolean isNewDefault = false;
+ NetworkAgentInfo oldDefaultNetwork = null;
if (DBG) log("rematching " + newNetwork.name());
// Find and migrate to this Network any NetworkRequests for
// which this network is now the best.
@@ -3869,8 +4102,7 @@
// check if it satisfies the NetworkCapabilities
if (VDBG) log(" checking if request is satisfied: " + nri.request);
- if (nri.request.networkCapabilities.satisfiedByNetworkCapabilities(
- newNetwork.networkCapabilities)) {
+ if (newNetwork.satisfies(nri.request)) {
if (!nri.isRequest) {
// This is not a request, it's a callback listener.
// Add it to newNetwork regardless of score.
@@ -3895,11 +4127,9 @@
} else {
if (DBG) log(" accepting network in place of null");
}
+ unlinger(newNetwork);
mNetworkForRequestId.put(nri.request.requestId, newNetwork);
newNetwork.addRequest(nri.request);
- if (nri.isRequest && nri.request.legacyType != TYPE_NONE) {
- mLegacyTypeTracker.add(nri.request.legacyType, newNetwork);
- }
keep = true;
// Tell NetworkFactories about the new score, so they can stop
// trying to connect if they know they cannot match it.
@@ -3909,54 +4139,18 @@
sendUpdatedScoreToFactories(nri.request, newNetwork.getCurrentScore());
if (mDefaultRequest.requestId == nri.request.requestId) {
isNewDefault = true;
- // TODO: Remove following line. It's redundant with makeDefault call.
- mActiveDefaultNetwork = newNetwork.networkInfo.getType();
- if (newNetwork.linkProperties != null) {
- updateTcpBufferSizes(newNetwork);
- setDefaultDnsSystemProperties(
- newNetwork.linkProperties.getDnsServers());
- } else {
- setDefaultDnsSystemProperties(new ArrayList<InetAddress>());
- }
- // Maintain the illusion: since the legacy API only
- // understands one network at a time, we must pretend
- // that the current default network disconnected before
- // the new one connected.
- if (currentNetwork != null) {
- mLegacyTypeTracker.remove(currentNetwork.networkInfo.getType(),
- currentNetwork);
- }
- mDefaultInetConditionPublished = newNetwork.validated ? 100 : 0;
- mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork);
- notifyLockdownVpn(newNetwork);
+ oldDefaultNetwork = currentNetwork;
}
}
}
}
// Linger any networks that are no longer needed.
for (NetworkAgentInfo nai : affectedNetworks) {
- boolean teardown = !nai.isVPN() && nai.validated;
- for (int i = 0; i < nai.networkRequests.size() && teardown; i++) {
- NetworkRequest nr = nai.networkRequests.valueAt(i);
- try {
- if (isRequest(nr)) {
- teardown = false;
- }
- } catch (Exception e) {
- loge("Request " + nr + " not found in mNetworkRequests.");
- loge(" it came from request list of " + nai.name());
- }
- }
- if (teardown) {
+ if (nai.everValidated && unneeded(nai)) {
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");
+ unlinger(nai);
}
}
if (keep) {
@@ -3976,6 +4170,24 @@
}
}
+ // do this after the default net is switched, but
+ // before LegacyTypeTracker sends legacy broadcasts
+ notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_AVAILABLE);
+
+ if (isNewDefault) {
+ // Maintain the illusion: since the legacy API only
+ // understands one network at a time, we must pretend
+ // that the current default network disconnected before
+ // the new one connected.
+ if (oldDefaultNetwork != null) {
+ mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(),
+ oldDefaultNetwork);
+ }
+ mDefaultInetConditionPublished = newNetwork.everValidated ? 100 : 0;
+ mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork);
+ notifyLockdownVpn(newNetwork);
+ }
+
// Notify battery stats service about this network, both the normal
// interface and any stacked links.
// TODO: Avoid redoing this; this must only be done once when a network comes online.
@@ -3993,18 +4205,53 @@
} catch (RemoteException ignored) {
}
- notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_AVAILABLE);
- } else if (nascent) {
+ // This has to happen after the notifyNetworkCallbacks as that tickles each
+ // ConnectivityManager instance so that legacy requests correctly bind dns
+ // requests to this network. The legacy users are listening for this bcast
+ // and will generally do a dns request so they can ensureRouteToHost and if
+ // they do that before the callbacks happen they'll use the default network.
+ //
+ // TODO: Is there still a race here? We send the broadcast
+ // after sending the callback, but if the app can receive the
+ // broadcast before the callback, it might still break.
+ //
+ // This *does* introduce a race where if the user uses the new api
+ // (notification callbacks) and then uses the old api (getNetworkInfo(type))
+ // they may get old info. Reverse this after the old startUsing api is removed.
+ // This is on top of the multiple intent sequencing referenced in the todo above.
+ for (int i = 0; i < newNetwork.networkRequests.size(); i++) {
+ NetworkRequest nr = newNetwork.networkRequests.valueAt(i);
+ if (nr.legacyType != TYPE_NONE && isRequest(nr)) {
+ // legacy type tracker filters out repeat adds
+ mLegacyTypeTracker.add(nr.legacyType, newNetwork);
+ }
+ }
+
+ // A VPN generally won't get added to the legacy tracker in the "for (nri)" loop above,
+ // because usually there are no NetworkRequests it satisfies (e.g., mDefaultRequest
+ // wants the NOT_VPN capability, so it will never be satisfied by a VPN). So, add the
+ // newNetwork to the tracker explicitly (it's a no-op if it has already been added).
+ if (newNetwork.isVPN()) {
+ mLegacyTypeTracker.add(TYPE_VPN, newNetwork);
+ }
+ } else if (nascent == NascentState.JUST_VALIDATED) {
// Only tear down newly validated networks here. Leave unvalidated to either become
- // validated (and get evaluated against peers, one losing here) or
- // NetworkMonitor reports a bad network and we tear it down then.
- // Networks that have been up for a while and are validated should be torn down via
- // the lingering process so communication on that network is given time to wrap up.
- // TODO: Could teardown unvalidated networks when their NetworkCapabilities
- // satisfy no NetworkRequests.
+ // validated (and get evaluated against peers, one losing here), or get reaped (see
+ // reapUnvalidatedNetworks) if they have no chance of becoming the highest scoring
+ // network. Networks that have been up for a while and are validated should be torn
+ // down via the lingering process so communication on that network is given time to
+ // wrap up.
if (DBG) log("Validated network turns out to be unwanted. Tear it down.");
teardownUnneededNetwork(newNetwork);
}
+ if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) {
+ for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ if (!nai.everValidated && unneeded(nai)) {
+ if (DBG) log("Reaping " + nai.name());
+ teardownUnneededNetwork(nai);
+ }
+ }
+ }
}
// Attempt to rematch all Networks with NetworkRequests. This may result in Networks
@@ -4025,22 +4272,29 @@
// can only add more NetworkRequests satisfied by "changed", and this is exactly what
// rematchNetworkAndRequests() handles.
if (changed != null && oldScore < changed.getCurrentScore()) {
- rematchNetworkAndRequests(changed, false);
+ rematchNetworkAndRequests(changed, NascentState.NOT_JUST_VALIDATED,
+ ReapUnvalidatedNetworks.REAP);
} else {
- for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- rematchNetworkAndRequests(nai, false);
+ for (Iterator i = mNetworkAgentInfos.values().iterator(); i.hasNext(); ) {
+ rematchNetworkAndRequests((NetworkAgentInfo)i.next(),
+ NascentState.NOT_JUST_VALIDATED,
+ // Only reap the last time through the loop. Reaping before all rematching
+ // is complete could incorrectly teardown a network that hasn't yet been
+ // rematched.
+ i.hasNext() ? ReapUnvalidatedNetworks.DONT_REAP
+ : ReapUnvalidatedNetworks.REAP);
}
}
}
- private void updateInetCondition(NetworkAgentInfo nai, boolean valid) {
+ private void updateInetCondition(NetworkAgentInfo nai) {
// Don't bother updating until we've graduated to validated at least once.
- if (!nai.validated) return;
+ if (!nai.everValidated) return;
// For now only update icons for default connection.
// TODO: Update WiFi and cellular icons separately. b/17237507
if (!isDefaultNetwork(nai)) return;
- int newInetCondition = valid ? 100 : 0;
+ int newInetCondition = nai.lastValidated ? 100 : 0;
// Don't repeat publish.
if (newInetCondition == mDefaultInetConditionPublished) return;
@@ -4098,6 +4352,7 @@
}
networkAgent.created = true;
updateLinkProperties(networkAgent, null);
+ notifyIfacesChanged();
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
if (networkAgent.isVPN()) {
@@ -4113,7 +4368,8 @@
// TODO: support proxy per network.
}
// Consider network even though it is not yet validated.
- rematchNetworkAndRequests(networkAgent, false);
+ rematchNetworkAndRequests(networkAgent, NascentState.NOT_JUST_VALIDATED,
+ ReapUnvalidatedNetworks.REAP);
} else if (state == NetworkInfo.State.DISCONNECTED ||
state == NetworkInfo.State.SUSPENDED) {
networkAgent.asyncChannel.disconnect();
@@ -4141,7 +4397,7 @@
final int oldScore = nai.getCurrentScore();
nai.setCurrentScore(score);
- if (nai.created) rematchAllNetworksAndRequests(nai, oldScore);
+ rematchAllNetworksAndRequests(nai, oldScore);
sendUpdatedScoreToFactories(nai);
}
@@ -4155,7 +4411,11 @@
// } else if (nai.networkMonitor.isEvaluating()) {
// notifyType = NetworkCallbacks.callCallbackForRequest(request, nai, notifyType);
// }
- callCallbackForRequest(nri, nai, notifyType);
+ if (nri.mPendingIntent == null) {
+ callCallbackForRequest(nri, nai, notifyType);
+ } else {
+ sendPendingIntentForRequest(nri, nai, notifyType);
+ }
}
private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, boolean connected, int type) {
@@ -4169,7 +4429,7 @@
info.setType(type);
if (connected) {
info.setDetailedState(DetailedState.CONNECTED, null, info.getExtraInfo());
- sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
+ sendConnectedBroadcast(info);
} else {
info.setDetailedState(DetailedState.DISCONNECTED, null, info.getExtraInfo());
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
@@ -4200,10 +4460,9 @@
final Intent immediateIntent = new Intent(intent);
immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
sendStickyBroadcast(immediateIntent);
- sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay());
+ sendStickyBroadcast(intent);
if (newDefaultAgent != null) {
- sendConnectedBroadcastDelayed(newDefaultAgent.networkInfo,
- getConnectivityChangeDelay());
+ sendConnectedBroadcast(newDefaultAgent.networkInfo);
}
}
}
@@ -4214,7 +4473,11 @@
NetworkRequest nr = networkAgent.networkRequests.valueAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
if (VDBG) log(" sending notification for " + nr);
- callCallbackForRequest(nri, networkAgent, notifyType);
+ if (nri.mPendingIntent == null) {
+ callCallbackForRequest(nri, networkAgent, notifyType);
+ } else {
+ sendPendingIntentForRequest(nri, networkAgent, notifyType);
+ }
}
}
@@ -4232,42 +4495,14 @@
return "UNKNOWN";
}
- private LinkProperties getLinkPropertiesForTypeInternal(int networkType) {
- NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
- if (nai != null) {
- synchronized (nai) {
- return new LinkProperties(nai.linkProperties);
- }
+ /**
+ * Notify other system services that set of active ifaces has changed.
+ */
+ private void notifyIfacesChanged() {
+ try {
+ mStatsService.forceUpdateIfaces();
+ } catch (Exception ignored) {
}
- 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 {
- NetworkInfo result = new NetworkInfo(
- networkType, 0, ConnectivityManager.getNetworkTypeName(networkType), "");
- result.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
- result.setIsAvailable(true);
- return result;
- }
- }
-
- private NetworkCapabilities getNetworkCapabilitiesForType(int networkType) {
- NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
- if (nai != null) {
- synchronized (nai) {
- return new NetworkCapabilities(nai.networkCapabilities);
- }
- }
- return new NetworkCapabilities();
}
@Override
@@ -4287,4 +4522,13 @@
return mVpns.get(user).removeAddress(address, prefixLength);
}
}
+
+ @Override
+ public boolean setUnderlyingNetworksForVpn(Network[] networks) {
+ throwIfLockdownEnabled();
+ int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized (mVpns) {
+ return mVpns.get(user).setUnderlyingNetworks(networks);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index c382be0..3fa21d0 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -17,6 +17,7 @@
package com.android.server.connectivity;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
import java.net.Inet4Address;
@@ -43,45 +44,41 @@
* Class to manage a 464xlat CLAT daemon.
*/
public class Nat464Xlat extends BaseNetworkObserver {
- private Context mContext;
- private INetworkManagementService mNMService;
- private IConnectivityManager mConnService;
- // 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";
-
private static final String TAG = "Nat464Xlat";
- public Nat464Xlat(Context context, INetworkManagementService nmService,
- IConnectivityManager connService, Handler handler) {
- mContext = context;
+ // This must match the interface prefix in clatd.c.
+ private static final String CLAT_PREFIX = "v4-";
+
+ private final INetworkManagementService mNMService;
+
+ // ConnectivityService Handler for LinkProperties updates.
+ private final Handler mHandler;
+
+ // The network we're running on, and its type.
+ private final NetworkAgentInfo mNetwork;
+
+ // Internal state variables.
+ //
+ // The possible states are:
+ // - Idle: start() not called. Everything is null.
+ // - Starting: start() called. Interfaces are non-null. isStarted() returns true.
+ // mIsRunning is false.
+ // - Running: start() called, and interfaceLinkStateChanged() told us that mIface is up.
+ // mIsRunning is true.
+ //
+ // Once mIface is non-null and isStarted() is true, methods called by ConnectivityService on
+ // its handler thread must not modify any internal state variables; they are only updated by the
+ // interface observers, called on the notification threads.
+ private String mBaseIface;
+ private String mIface;
+ private boolean mIsRunning;
+
+ public Nat464Xlat(
+ Context context, INetworkManagementService nmService,
+ Handler handler, NetworkAgentInfo nai) {
mNMService = nmService;
- mConnService = connService;
mHandler = handler;
-
- 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.
+ mNetwork = nai;
}
/**
@@ -94,137 +91,196 @@
final boolean connected = nai.networkInfo.isConnected();
final boolean hasIPv4Address =
(nai.linkProperties != null) ? nai.linkProperties.hasIPv4Address() : false;
- Slog.d(TAG, "requiresClat: netType=" + netType +
- ", connected=" + connected +
- ", hasIPv4Address=" + hasIPv4Address);
- // Only support clat on mobile for now.
- return netType == TYPE_MOBILE && connected && !hasIPv4Address;
- }
-
- public boolean isRunningClat(NetworkAgentInfo network) {
- return mNetworkMessenger == network.messenger;
+ // Only support clat on mobile and wifi for now, because these are the only IPv6-only
+ // networks we can connect to.
+ return connected && !hasIPv4Address && (netType == TYPE_MOBILE || netType == TYPE_WIFI);
}
/**
- * Starts the clat daemon.
- * @param lp The link properties of the interface to start clatd on.
+ * Determines whether clatd is started. Always true, except a) if start has not yet been called,
+ * or b) if our interface was removed.
*/
- 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) {
+ public boolean isStarted() {
+ return mIface != null;
+ }
+
+ /**
+ * Clears internal state. Must not be called by ConnectivityService.
+ */
+ private void clear() {
+ mIface = null;
+ mBaseIface = null;
+ mIsRunning = false;
+ }
+
+ /**
+ * Starts the clat daemon. Called by ConnectivityService on the handler thread.
+ */
+ public void start() {
+ if (isStarted()) {
Slog.e(TAG, "startClat: already started");
return;
}
- String iface = lp.getInterfaceName();
- Slog.i(TAG, "Starting clatd on " + iface + ", lp=" + lp);
- try {
- mNMService.startClatd(iface);
- } catch(RemoteException e) {
- Slog.e(TAG, "Error starting clat daemon: " + e);
+
+ if (mNetwork.linkProperties == null) {
+ Slog.e(TAG, "startClat: Can't start clat with null LinkProperties");
+ return;
}
- mIsStarted = true;
+
+ try {
+ mNMService.registerObserver(this);
+ } catch(RemoteException e) {
+ Slog.e(TAG, "startClat: Can't register interface observer for clat on " + mNetwork);
+ return;
+ }
+
+ mBaseIface = mNetwork.linkProperties.getInterfaceName();
+ if (mBaseIface == null) {
+ Slog.e(TAG, "startClat: Can't start clat on null interface");
+ return;
+ }
+ mIface = CLAT_PREFIX + mBaseIface;
+ // From now on, isStarted() will return true.
+
+ Slog.i(TAG, "Starting clatd on " + mBaseIface);
+ try {
+ mNMService.startClatd(mBaseIface);
+ } catch(RemoteException|IllegalStateException e) {
+ Slog.e(TAG, "Error starting clatd: " + e);
+ }
}
/**
- * Stops the clat daemon.
+ * Stops the clat daemon. Called by ConnectivityService on the handler thread.
*/
- public void stopClat() {
- if (mIsStarted) {
+ public void stop() {
+ if (isStarted()) {
Slog.i(TAG, "Stopping clatd");
try {
- mNMService.stopClatd();
- } catch(RemoteException e) {
- Slog.e(TAG, "Error stopping clat daemon: " + e);
+ mNMService.stopClatd(mBaseIface);
+ } catch(RemoteException|IllegalStateException e) {
+ Slog.e(TAG, "Error stopping clatd: " + e);
}
- mIsStarted = false;
- mIsRunning = false;
- mNetworkMessenger = null;
- mBaseLP = null;
- mLP.clear();
+ // When clatd stops and its interface is deleted, interfaceRemoved() will notify
+ // ConnectivityService and call clear().
} else {
- Slog.e(TAG, "stopClat: already stopped");
+ Slog.e(TAG, "clatd: already stopped");
}
}
- private void updateConnectivityService() {
- Message msg = mHandler.obtainMessage(
- NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED, mBaseLP);
- msg.replyTo = mNetworkMessenger;
+ private void updateConnectivityService(LinkProperties lp) {
+ Message msg = mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED, lp);
+ msg.replyTo = mNetwork.messenger;
Slog.i(TAG, "sending message to ConnectivityService: " + msg);
msg.sendToTarget();
}
- // Copies the stacked clat link in oldLp, if any, to the LinkProperties in nai.
- public void fixupLinkProperties(NetworkAgentInfo nai, LinkProperties oldLp) {
- if (isRunningClat(nai) &&
- nai.linkProperties != null &&
- !nai.linkProperties.getAllInterfaceNames().contains(CLAT_INTERFACE_NAME)) {
- Slog.d(TAG, "clatd running, updating NAI for " + nai.linkProperties.getInterfaceName());
+ /**
+ * Copies the stacked clat link in oldLp, if any, to the LinkProperties in mNetwork.
+ * This is necessary because the LinkProperties in mNetwork come from the transport layer, which
+ * has no idea that 464xlat is running on top of it.
+ */
+ public void fixupLinkProperties(LinkProperties oldLp) {
+ if (mNetwork.clatd != null &&
+ mIsRunning &&
+ mNetwork.linkProperties != null &&
+ !mNetwork.linkProperties.getAllInterfaceNames().contains(mIface)) {
+ Slog.d(TAG, "clatd running, updating NAI for " + mIface);
for (LinkProperties stacked: oldLp.getStackedLinks()) {
- if (CLAT_INTERFACE_NAME.equals(stacked.getInterfaceName())) {
- nai.linkProperties.addStackedLink(stacked);
+ if (mIface.equals(stacked.getInterfaceName())) {
+ mNetwork.linkProperties.addStackedLink(stacked);
break;
}
}
}
}
+ private LinkProperties makeLinkProperties(LinkAddress clatAddress) {
+ LinkProperties stacked = new LinkProperties();
+ stacked.setInterfaceName(mIface);
+
+ // Although the clat interface is a point-to-point tunnel, we don't
+ // point the route directly at the interface because some apps don't
+ // understand routes without gateways (see, e.g., http://b/9597256
+ // http://b/9597516). Instead, set the next hop of the route to the
+ // clat IPv4 address itself (for those apps, it doesn't matter what
+ // the IP of the gateway is, only that there is one).
+ RouteInfo ipv4Default = new RouteInfo(
+ new LinkAddress(Inet4Address.ANY, 0),
+ clatAddress.getAddress(), mIface);
+ stacked.addRoute(ipv4Default);
+ stacked.addLinkAddress(clatAddress);
+ return stacked;
+ }
+
+ private LinkAddress getLinkAddress(String iface) {
+ try {
+ InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
+ return config.getLinkAddress();
+ } catch(RemoteException|IllegalStateException e) {
+ Slog.e(TAG, "Error getting link properties: " + e);
+ return null;
+ }
+ }
+
+ private void maybeSetIpv6NdOffload(String iface, boolean on) {
+ if (mNetwork.networkInfo.getType() != TYPE_WIFI) {
+ return;
+ }
+ try {
+ Slog.d(TAG, (on ? "En" : "Dis") + "abling ND offload on " + iface);
+ mNMService.setInterfaceIpv6NdOffload(iface, on);
+ } catch(RemoteException|IllegalStateException e) {
+ Slog.w(TAG, "Changing IPv6 ND offload on " + iface + "failed: " + e);
+ }
+ }
+
@Override
- public void interfaceAdded(String iface) {
- if (iface.equals(CLAT_INTERFACE_NAME)) {
- Slog.i(TAG, "interface " + CLAT_INTERFACE_NAME +
- " added, mIsRunning = " + mIsRunning + " -> true");
- mIsRunning = true;
+ public void interfaceLinkStateChanged(String iface, boolean up) {
+ // Called by the InterfaceObserver on its own thread, so can race with stop().
+ if (isStarted() && up && mIface.equals(iface)) {
+ Slog.i(TAG, "interface " + iface + " is up, mIsRunning " + mIsRunning + "->true");
- // Create the LinkProperties for the clat interface by fetching the
- // IPv4 address for the interface and adding an IPv4 default route,
- // then stack the LinkProperties on top of the link it's running on.
- try {
- InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
- LinkAddress clatAddress = config.getLinkAddress();
- mLP.clear();
- mLP.setInterfaceName(iface);
-
- // Although the clat interface is a point-to-point tunnel, we don't
- // point the route directly at the interface because some apps don't
- // understand routes without gateways (see, e.g., http://b/9597256
- // http://b/9597516). Instead, set the next hop of the route to the
- // clat IPv4 address itself (for those apps, it doesn't matter what
- // the IP of the gateway is, only that there is one).
- RouteInfo ipv4Default = new RouteInfo(new LinkAddress(Inet4Address.ANY, 0),
- clatAddress.getAddress(), iface);
- mLP.addRoute(ipv4Default);
- mLP.addLinkAddress(clatAddress);
- mBaseLP.addStackedLink(mLP);
- Slog.i(TAG, "Adding stacked link. tracker LP: " + mBaseLP);
- updateConnectivityService();
- } catch(RemoteException e) {
- Slog.e(TAG, "Error getting link properties: " + e);
+ if (!mIsRunning) {
+ LinkAddress clatAddress = getLinkAddress(iface);
+ if (clatAddress == null) {
+ return;
+ }
+ mIsRunning = true;
+ maybeSetIpv6NdOffload(mBaseIface, false);
+ LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
+ lp.addStackedLink(makeLinkProperties(clatAddress));
+ Slog.i(TAG, "Adding stacked link " + mIface + " on top of " + mBaseIface);
+ updateConnectivityService(lp);
}
}
}
@Override
public void interfaceRemoved(String iface) {
- if (iface == CLAT_INTERFACE_NAME) {
+ if (isStarted() && mIface.equals(iface)) {
+ Slog.i(TAG, "interface " + iface + " removed, mIsRunning " + mIsRunning + "->false");
+
if (mIsRunning) {
- NetworkUtils.resetConnections(
- CLAT_INTERFACE_NAME,
- NetworkUtils.RESET_IPV4_ADDRESSES);
- mBaseLP.removeStackedLink(mLP);
- updateConnectivityService();
+ // The interface going away likely means clatd has crashed. Ask netd to stop it,
+ // because otherwise when we try to start it again on the same base interface netd
+ // will complain that it's already started.
+ //
+ // Note that this method can be called by the interface observer at the same time
+ // that ConnectivityService calls stop(). In this case, the second call to
+ // stopClatd() will just throw IllegalStateException, which we'll ignore.
+ try {
+ mNMService.unregisterObserver(this);
+ mNMService.stopClatd(mBaseIface);
+ } catch (RemoteException|IllegalStateException e) {
+ // Well, we tried.
+ }
+ maybeSetIpv6NdOffload(mBaseIface, true);
+ LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
+ lp.removeStackedLink(mIface);
+ clear();
+ updateConnectivityService(lp);
}
- Slog.i(TAG, "interface " + CLAT_INTERFACE_NAME +
- " removed, mIsRunning = " + mIsRunning + " -> false");
- mIsRunning = false;
- 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
index 15ffc0d..f3e0bbc 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -45,8 +45,22 @@
public NetworkCapabilities networkCapabilities;
public final NetworkMonitor networkMonitor;
public final NetworkMisc networkMisc;
+ // Indicates if netd has been told to create this Network. Once created the appropriate routing
+ // rules are setup and routes are added so packets can begin flowing over the Network.
+ // NOTE: This is a sticky bit; once set it is never cleared.
public boolean created;
- public boolean validated;
+ // Set to true if this Network successfully passed validation or if it did not satisfy the
+ // default NetworkRequest in which case validation will not be attempted.
+ // NOTE: This is a sticky bit; once set it is never cleared even if future validation attempts
+ // fail.
+ public boolean everValidated;
+
+ // The result of the last validation attempt on this network (true if validated, false if not).
+ // This bit exists only because we never unvalidate a network once it's been validated, and that
+ // is because the network scoring and revalidation code does not (may not?) deal properly with
+ // networks becoming unvalidated.
+ // TODO: Fix the network scoring code, remove this, and rename everValidated to validated.
+ public boolean lastValidated;
// This represents the last score received from the NetworkAgent.
private int currentScore;
@@ -58,14 +72,20 @@
// The list of NetworkRequests being satisfied by this Network.
public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
+ // The list of NetworkRequests that this Network previously satisfied with the highest
+ // score. A non-empty list indicates that if this Network was validated it is lingered.
+ // NOTE: This list is only used for debugging.
public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>();
public final Messenger messenger;
public final AsyncChannel asyncChannel;
+ // Used by ConnectivityService to keep track of 464xlat.
+ public Nat464Xlat clatd;
+
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
- NetworkMisc misc) {
+ NetworkMisc misc, NetworkRequest defaultRequest) {
this.messenger = messenger;
asyncChannel = ac;
network = null;
@@ -73,23 +93,28 @@
linkProperties = lp;
networkCapabilities = nc;
currentScore = score;
- networkMonitor = new NetworkMonitor(context, handler, this);
+ networkMonitor = new NetworkMonitor(context, handler, this, defaultRequest);
networkMisc = misc;
created = false;
- validated = false;
+ everValidated = false;
+ lastValidated = false;
}
public void addRequest(NetworkRequest networkRequest) {
networkRequests.put(networkRequest.requestId, networkRequest);
}
+ // Does this network satisfy request?
+ public boolean satisfies(NetworkRequest request) {
+ return created &&
+ request.networkCapabilities.satisfiedByNetworkCapabilities(networkCapabilities);
+ }
+
public boolean isVPN() {
return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
}
- // Get the current score for this Network. This may be modified from what the
- // NetworkAgent sent, as it has modifiers applied to it.
- public int getCurrentScore() {
+ private int getCurrentScore(boolean pretendValidated) {
// TODO: We may want to refactor this into a NetworkScore class that takes a base score from
// the NetworkAgent and signals from the NetworkAgent and uses those signals to modify the
// score. The NetworkScore class would provide a nice place to centralize score constants
@@ -97,7 +122,7 @@
int score = currentScore;
- if (!validated) score -= UNVALIDATED_SCORE_PENALTY;
+ if (!everValidated && !pretendValidated) score -= UNVALIDATED_SCORE_PENALTY;
if (score < 0) score = 0;
if (networkMisc.explicitlySelected) score = EXPLICITLY_SELECTED_NETWORK_SCORE;
@@ -105,6 +130,18 @@
return score;
}
+ // Get the current score for this Network. This may be modified from what the
+ // NetworkAgent sent, as it has modifiers applied to it.
+ public int getCurrentScore() {
+ return getCurrentScore(false);
+ }
+
+ // Get the current score for this Network as if it was validated. This may be modified from
+ // what the NetworkAgent sent, as it has modifiers applied to it.
+ public int getCurrentScoreAsValidated() {
+ return getCurrentScore(true);
+ }
+
public void setCurrentScore(int newScore) {
currentScore = newScore;
}
@@ -113,8 +150,9 @@
return "NetworkAgentInfo{ ni{" + networkInfo + "} network{" +
network + "} lp{" +
linkProperties + "} nc{" +
- networkCapabilities + "} Score{" + getCurrentScore() + "} " +
- "validated{" + validated + "} created{" + created + "} " +
+ networkCapabilities + "} Score{" + getCurrentScore() + "} " +
+ "everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} " +
+ "created{" + created + "} " +
"explicitlySelected{" + networkMisc.explicitlySelected + "} }";
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index c3d4ed9..7383478 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -932,7 +932,6 @@
expect(mSettings.getPollInterval()).andReturn(HOUR_IN_MILLIS).anyTimes();
expect(mSettings.getTimeCacheMaxAge()).andReturn(DAY_IN_MILLIS).anyTimes();
expect(mSettings.getSampleEnabled()).andReturn(true).anyTimes();
- expect(mSettings.getReportXtOverDev()).andReturn(true).anyTimes();
final Config config = new Config(bucketDuration, deleteAge, deleteAge);
expect(mSettings.getDevConfig()).andReturn(config).anyTimes();
@@ -1007,7 +1006,7 @@
info.setDetailedState(DetailedState.CONNECTED, null, null);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
- return new NetworkState(info, prop, null, null, TEST_SSID);
+ return new NetworkState(info, prop, null, null, null, TEST_SSID);
}
private static NetworkState buildMobile3gState(String subscriberId) {
@@ -1016,7 +1015,7 @@
info.setDetailedState(DetailedState.CONNECTED, null, null);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
- return new NetworkState(info, prop, null, subscriberId, null);
+ return new NetworkState(info, prop, null, null, subscriberId, null);
}
private static NetworkState buildMobile4gState(String iface) {
@@ -1024,7 +1023,7 @@
info.setDetailedState(DetailedState.CONNECTED, null, null);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(iface);
- return new NetworkState(info, prop, null);
+ return new NetworkState(info, prop, null, null, null, null);
}
private NetworkStats buildEmptyStats() {