am 3c73a8e8: (-s ours) am ca4ba835: am 41708e1c: DO NOT MERGE Sanitize WifiConfigs
* commit '3c73a8e87a43b6c7159f0b531948dae8b3686dfa':
DO NOT MERGE Sanitize WifiConfigs
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 02a6494..c78a973 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -355,11 +355,17 @@
*/
public static final int TYPE_WIFI_P2P = 13;
- /** {@hide} */
- public static final int MAX_RADIO_TYPE = TYPE_WIFI_P2P;
+ /**
+ * The network to use for initially attaching to the network
+ * {@hide}
+ */
+ public static final int TYPE_MOBILE_IA = 14;
/** {@hide} */
- public static final int MAX_NETWORK_TYPE = TYPE_WIFI_P2P;
+ public static final int MAX_RADIO_TYPE = TYPE_MOBILE_IA;
+
+ /** {@hide} */
+ public static final int MAX_NETWORK_TYPE = TYPE_MOBILE_IA;
/**
* If you want to set the default network preference,you can directly
@@ -436,6 +442,8 @@
return "MOBILE_CBS";
case TYPE_WIFI_P2P:
return "WIFI_P2P";
+ case TYPE_MOBILE_IA:
+ return "MOBILE_IA";
default:
return Integer.toString(type);
}
@@ -458,6 +466,39 @@
case TYPE_MOBILE_FOTA:
case TYPE_MOBILE_IMS:
case TYPE_MOBILE_CBS:
+ case TYPE_MOBILE_IA:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the given network type is backed by a Wi-Fi radio.
+ *
+ * @hide
+ */
+ public static boolean isNetworkTypeWifi(int networkType) {
+ switch (networkType) {
+ case TYPE_WIFI:
+ case TYPE_WIFI_P2P:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the given network type should be exempt from VPN routing rules
+ *
+ * @hide
+ */
+ public static boolean isNetworkTypeExempt(int networkType) {
+ switch (networkType) {
+ case TYPE_MOBILE_MMS:
+ case TYPE_MOBILE_SUPL:
+ case TYPE_MOBILE_HIPRI:
+ case TYPE_MOBILE_IA:
return true;
default:
return false;
@@ -1382,6 +1423,45 @@
}
/**
+ * 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
@@ -1396,4 +1476,20 @@
} catch (RemoteException e) {
}
}
+
+ /**
+ * Set the value for enabling/disabling airplane mode
+ *
+ * @param enable whether to enable airplane mode or not
+ *
+ * <p>This method requires the call to hold the permission
+ * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
+ * @hide
+ */
+ public void setAirplaneMode(boolean enable) {
+ try {
+ mService.setAirplaneMode(enable);
+ } catch (RemoteException e) {
+ }
+ }
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index a17b4f5..c1da2e3 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -16,6 +16,7 @@
package android.net;
+import android.net.LinkQualityInfo;
import android.net.LinkProperties;
import android.net.NetworkInfo;
import android.net.NetworkQuotaInfo;
@@ -37,6 +38,9 @@
/** {@hide} */
interface IConnectivityManager
{
+ // Keep this in sync with framework/native/services/connectivitymanager/ConnectivityManager.h
+ void markSocketAsUser(in ParcelFileDescriptor socket, int uid);
+
void setNetworkPreference(int pref);
int getNetworkPreference();
@@ -89,12 +93,6 @@
String[] getTetheredIfaces();
- /**
- * Return list of interface pairs that are actively tethered. Even indexes are
- * remote interface, and odd indexes are corresponding local interfaces.
- */
- String[] getTetheredIfacePairs();
-
String[] getTetheringErroredIfaces();
String[] getTetherableUsbRegexs();
@@ -123,6 +121,8 @@
ParcelFileDescriptor establishVpn(in VpnConfig config);
+ VpnConfig getVpnConfig();
+
void startLegacyVpn(in VpnProfile profile);
LegacyVpnInfo getLegacyVpnInfo();
@@ -143,5 +143,13 @@
String getMobileRedirectedProvisioningUrl();
+ LinkQualityInfo getLinkQualityInfo(int networkType);
+
+ LinkQualityInfo getActiveLinkQualityInfo();
+
+ LinkQualityInfo[] getAllLinkQualityInfo();
+
void setProvisioningNotificationVisible(boolean visible, int networkType, in String extraInfo, in String url);
+
+ void setAirplaneMode(boolean enable);
}
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index f6a114c..a390add 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -32,27 +32,56 @@
/**
* IPv4 or IPv6 address.
*/
- private final InetAddress address;
+ private InetAddress address;
/**
* Network prefix length
*/
- private final int prefixLength;
+ private int prefixLength;
- public LinkAddress(InetAddress address, int prefixLength) {
+ private void init(InetAddress address, int prefixLength) {
if (address == null || prefixLength < 0 ||
((address instanceof Inet4Address) && prefixLength > 32) ||
(prefixLength > 128)) {
throw new IllegalArgumentException("Bad LinkAddress params " + address +
- prefixLength);
+ "/" + prefixLength);
}
this.address = address;
this.prefixLength = prefixLength;
}
+ public LinkAddress(InetAddress address, int prefixLength) {
+ init(address, prefixLength);
+ }
+
public LinkAddress(InterfaceAddress interfaceAddress) {
- this.address = interfaceAddress.getAddress();
- this.prefixLength = interfaceAddress.getNetworkPrefixLength();
+ init(interfaceAddress.getAddress(),
+ interfaceAddress.getNetworkPrefixLength());
+ }
+
+ /**
+ * Constructs a new {@code LinkAddress} from a string such as "192.0.2.5/24" or
+ * "2001:db8::1/64".
+ * @param string The string to parse.
+ */
+ public LinkAddress(String address) {
+ InetAddress inetAddress = null;
+ int prefixLength = -1;
+ try {
+ String [] pieces = address.split("/", 2);
+ prefixLength = Integer.parseInt(pieces[1]);
+ inetAddress = InetAddress.parseNumericAddress(pieces[0]);
+ } catch (NullPointerException e) { // Null string.
+ } catch (ArrayIndexOutOfBoundsException e) { // No prefix length.
+ } catch (NumberFormatException e) { // Non-numeric prefix.
+ } catch (IllegalArgumentException e) { // Invalid IP address.
+ }
+
+ if (inetAddress == null || prefixLength == -1) {
+ throw new IllegalArgumentException("Bad LinkAddress params " + address);
+ }
+
+ init(inetAddress, prefixLength);
}
@Override
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index dc9a54f..b4d07a1 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -23,6 +23,7 @@
import java.net.InetAddress;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.UnknownHostException;
import java.util.ArrayList;
@@ -65,6 +66,7 @@
private String mDomains;
private Collection<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
private ProxyProperties mHttpProxy;
+ private int mMtu;
// Stores the properties of links that are "stacked" above this link.
// Indexed by interface name to allow modification and to prevent duplicates being added.
@@ -103,6 +105,7 @@
for (LinkProperties l: source.mStackedLinks.values()) {
addStackedLink(l);
}
+ setMtu(source.getMtu());
}
}
@@ -128,6 +131,9 @@
return interfaceNames;
}
+ /**
+ * Returns all the addresses on this link.
+ */
public Collection<InetAddress> getAddresses() {
Collection<InetAddress> addresses = new ArrayList<InetAddress>();
for (LinkAddress linkAddress : mLinkAddresses) {
@@ -136,16 +142,65 @@
return Collections.unmodifiableCollection(addresses);
}
- public void addLinkAddress(LinkAddress address) {
- if (address != null) mLinkAddresses.add(address);
+ /**
+ * Returns all the addresses on this link and all the links stacked above it.
+ */
+ public Collection<InetAddress> getAllAddresses() {
+ Collection<InetAddress> addresses = new ArrayList<InetAddress>();
+ for (LinkAddress linkAddress : mLinkAddresses) {
+ addresses.add(linkAddress.getAddress());
+ }
+ for (LinkProperties stacked: mStackedLinks.values()) {
+ addresses.addAll(stacked.getAllAddresses());
+ }
+ return addresses;
}
+ /**
+ * Adds a link address if it does not exist, or update it if it does.
+ * @param address The {@code LinkAddress} to add.
+ * @return true if the address was added, false if it already existed.
+ */
+ public boolean addLinkAddress(LinkAddress address) {
+ // TODO: when the LinkAddress has other attributes beyond the
+ // address and the prefix length, update them here.
+ if (address != null && !mLinkAddresses.contains(address)) {
+ mLinkAddresses.add(address);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Removes a link address.
+ * @param address The {@code LinkAddress} to remove.
+ * @return true if the address was removed, false if it did not exist.
+ */
+ public boolean removeLinkAddress(LinkAddress toRemove) {
+ return mLinkAddresses.remove(toRemove);
+ }
+
+ /**
+ * Returns all the addresses on this link.
+ */
public Collection<LinkAddress> getLinkAddresses() {
return Collections.unmodifiableCollection(mLinkAddresses);
}
/**
- * Replaces the LinkAddresses on this link with the given collection of addresses
+ * Returns all the addresses on this link and all the links stacked above it.
+ */
+ public Collection<LinkAddress> getAllLinkAddresses() {
+ Collection<LinkAddress> addresses = new ArrayList<LinkAddress>();
+ addresses.addAll(mLinkAddresses);
+ for (LinkProperties stacked: mStackedLinks.values()) {
+ addresses.addAll(stacked.getAllLinkAddresses());
+ }
+ return addresses;
+ }
+
+ /**
+ * Replaces the LinkAddresses on this link with the given collection of addresses.
*/
public void setLinkAddresses(Collection<LinkAddress> addresses) {
mLinkAddresses.clear();
@@ -170,6 +225,14 @@
mDomains = domains;
}
+ public void setMtu(int mtu) {
+ mMtu = mtu;
+ }
+
+ public int getMtu() {
+ return mMtu;
+ }
+
private RouteInfo routeWithInterface(RouteInfo route) {
return new RouteInfo(
route.getDestination(),
@@ -208,16 +271,6 @@
return routes;
}
- /**
- * Replaces the RouteInfos on this link with the given collection of RouteInfos.
- */
- public void setRoutes(Collection<RouteInfo> routes) {
- mRoutes.clear();
- for (RouteInfo route : routes) {
- addRoute(route);
- }
- }
-
public void setHttpProxy(ProxyProperties proxy) {
mHttpProxy = proxy;
}
@@ -233,11 +286,14 @@
* of stacked links. If link is null, nothing changes.
*
* @param link The link to add.
+ * @return true if the link was stacked, false otherwise.
*/
- public void addStackedLink(LinkProperties link) {
+ public boolean addStackedLink(LinkProperties link) {
if (link != null && link.getInterfaceName() != null) {
mStackedLinks.put(link.getInterfaceName(), link);
+ return true;
}
+ return false;
}
/**
@@ -246,12 +302,15 @@
* If there a stacked link with the same interfacename as link, it is
* removed. Otherwise, nothing changes.
*
- * @param link The link to add.
+ * @param link The link to remove.
+ * @return true if the link was removed, false otherwise.
*/
- public void removeStackedLink(LinkProperties link) {
+ public boolean removeStackedLink(LinkProperties link) {
if (link != null && link.getInterfaceName() != null) {
- mStackedLinks.remove(link.getInterfaceName());
+ LinkProperties removed = mStackedLinks.remove(link.getInterfaceName());
+ return removed != null;
}
+ return false;
}
/**
@@ -273,6 +332,7 @@
mRoutes.clear();
mHttpProxy = null;
mStackedLinks.clear();
+ mMtu = 0;
}
/**
@@ -297,6 +357,8 @@
String domainName = "Domains: " + mDomains;
+ String mtu = "MTU: " + mMtu;
+
String routes = " Routes: [";
for (RouteInfo route : mRoutes) routes += route.toString() + ",";
routes += "] ";
@@ -310,7 +372,8 @@
}
stacked += "] ";
}
- return "{" + ifaceName + linkAddresses + routes + dns + domainName + proxy + stacked + "}";
+ return "{" + ifaceName + linkAddresses + routes + dns + domainName + mtu
+ + proxy + stacked + "}";
}
/**
@@ -328,6 +391,20 @@
}
/**
+ * Returns true if this link has an IPv6 address.
+ *
+ * @return {@code true} if there is an IPv6 address, {@code false} otherwise.
+ */
+ public boolean hasIPv6Address() {
+ for (LinkAddress address : mLinkAddresses) {
+ if (address.getAddress() instanceof Inet6Address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Compares this {@code LinkProperties} interface name against the target
*
* @param target LinkProperties to compare.
@@ -411,6 +488,16 @@
return true;
}
+ /**
+ * Compares this {@code LinkProperties} MTU against the target
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ */
+ public boolean isIdenticalMtu(LinkProperties target) {
+ return getMtu() == target.getMtu();
+ }
+
@Override
/**
* Compares this {@code LinkProperties} instance against the target
@@ -442,17 +529,16 @@
isIdenticalDnses(target) &&
isIdenticalRoutes(target) &&
isIdenticalHttpProxy(target) &&
- isIdenticalStackedLinks(target);
+ isIdenticalStackedLinks(target) &&
+ isIdenticalMtu(target);
}
/**
- * Return two lists, a list of addresses that would be removed from
- * mLinkAddresses and a list of addresses that would be added to
- * mLinkAddress which would then result in target and mLinkAddresses
- * being the same list.
+ * Compares the addresses in this LinkProperties with another
+ * LinkProperties, examining only addresses on the base link.
*
- * @param target is a LinkProperties with the new list of addresses
- * @return the removed and added lists.
+ * @param target a LinkProperties with the new list of addresses
+ * @return the differences between the addresses.
*/
public CompareResult<LinkAddress> compareAddresses(LinkProperties target) {
/*
@@ -476,13 +562,11 @@
}
/**
- * Return two lists, a list of dns addresses that would be removed from
- * mDnses and a list of addresses that would be added to
- * mDnses which would then result in target and mDnses
- * being the same list.
+ * Compares the DNS addresses in this LinkProperties with another
+ * LinkProperties, examining only DNS addresses on the base link.
*
- * @param target is a LinkProperties with the new list of dns addresses
- * @return the removed and added lists.
+ * @param target a LinkProperties with the new list of dns addresses
+ * @return the differences between the DNS addresses.
*/
public CompareResult<InetAddress> compareDnses(LinkProperties target) {
/*
@@ -507,15 +591,13 @@
}
/**
- * Return two lists, a list of routes that would be removed from
- * mRoutes and a list of routes that would be added to
- * mRoutes which would then result in target and mRoutes
- * being the same list.
+ * Compares all routes in this LinkProperties with another LinkProperties,
+ * examining both the the base link and all stacked links.
*
- * @param target is a LinkProperties with the new list of routes
- * @return the removed and added lists.
+ * @param target a LinkProperties with the new list of routes
+ * @return the differences between the routes.
*/
- public CompareResult<RouteInfo> compareRoutes(LinkProperties target) {
+ public CompareResult<RouteInfo> compareAllRoutes(LinkProperties target) {
/*
* Duplicate the RouteInfos into removed, we will be removing
* routes which are common between mRoutes and target
@@ -550,7 +632,8 @@
+ ((null == mDomains) ? 0 : mDomains.hashCode())
+ mRoutes.size() * 41
+ ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode())
- + mStackedLinks.hashCode() * 47);
+ + mStackedLinks.hashCode() * 47)
+ + mMtu * 51;
}
/**
@@ -568,7 +651,7 @@
dest.writeByteArray(d.getAddress());
}
dest.writeString(mDomains);
-
+ dest.writeInt(mMtu);
dest.writeInt(mRoutes.size());
for(RouteInfo route : mRoutes) {
dest.writeParcelable(route, flags);
@@ -607,6 +690,7 @@
} catch (UnknownHostException e) { }
}
netProp.setDomains(in.readString());
+ netProp.setMtu(in.readInt());
addressCount = in.readInt();
for (int i=0; i<addressCount; i++) {
netProp.addRoute((RouteInfo)in.readParcelable(null));
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 4ab479e..b24d396 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -21,6 +21,7 @@
import java.net.Inet6Address;
import java.net.UnknownHostException;
import java.util.Collection;
+import java.util.Locale;
import android.util.Log;
@@ -103,6 +104,11 @@
public native static String getDhcpError();
/**
+ * Set the SO_MARK of {@code socketfd} to {@code mark}
+ */
+ public native static void markSocket(int socketfd, int mark);
+
+ /**
* Convert a IPv4 address from an integer to an InetAddress.
* @param hostAddress an int corresponding to the IPv4 address in network byte order
*/
@@ -223,7 +229,7 @@
public static InetAddress hexToInet6Address(String addrHexString)
throws IllegalArgumentException {
try {
- return numericToInetAddress(String.format("%s:%s:%s:%s:%s:%s:%s:%s",
+ return numericToInetAddress(String.format(Locale.US, "%s:%s:%s:%s:%s:%s:%s:%s",
addrHexString.substring(0,4), addrHexString.substring(4,8),
addrHexString.substring(8,12), addrHexString.substring(12,16),
addrHexString.substring(16,20), addrHexString.substring(20,24),
diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyProperties.java
index 9c4772b..010e527 100644
--- a/core/java/android/net/ProxyProperties.java
+++ b/core/java/android/net/ProxyProperties.java
@@ -20,9 +20,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
-import android.util.Log;
-import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Locale;
@@ -38,17 +36,38 @@
private String mExclusionList;
private String[] mParsedExclusionList;
+ private String mPacFileUrl;
+ public static final String LOCAL_EXCL_LIST = "";
+ public static final int LOCAL_PORT = -1;
+ public static final String LOCAL_HOST = "localhost";
+
public ProxyProperties(String host, int port, String exclList) {
mHost = host;
mPort = port;
setExclusionList(exclList);
}
+ public ProxyProperties(String pacFileUrl) {
+ mHost = LOCAL_HOST;
+ mPort = LOCAL_PORT;
+ setExclusionList(LOCAL_EXCL_LIST);
+ mPacFileUrl = pacFileUrl;
+ }
+
+ // Only used in PacManager after Local Proxy is bound.
+ public ProxyProperties(String pacFileUrl, int localProxyPort) {
+ mHost = LOCAL_HOST;
+ mPort = localProxyPort;
+ setExclusionList(LOCAL_EXCL_LIST);
+ mPacFileUrl = pacFileUrl;
+ }
+
private ProxyProperties(String host, int port, String exclList, String[] parsedExclList) {
mHost = host;
mPort = port;
mExclusionList = exclList;
mParsedExclusionList = parsedExclList;
+ mPacFileUrl = null;
}
// copy constructor instead of clone
@@ -56,6 +75,7 @@
if (source != null) {
mHost = source.getHost();
mPort = source.getPort();
+ mPacFileUrl = source.getPacFileUrl();
mExclusionList = source.getExclusionList();
mParsedExclusionList = source.mParsedExclusionList;
}
@@ -69,6 +89,10 @@
return inetSocketAddress;
}
+ public String getPacFileUrl() {
+ return mPacFileUrl;
+ }
+
public String getHost() {
return mHost;
}
@@ -115,6 +139,17 @@
return false;
}
+ public boolean isValid() {
+ if (!TextUtils.isEmpty(mPacFileUrl)) return true;
+ try {
+ Proxy.validate(mHost == null ? "" : mHost, mPort == 0 ? "" : Integer.toString(mPort),
+ mExclusionList == null ? "" : mExclusionList);
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ return true;
+ }
+
public java.net.Proxy makeProxy() {
java.net.Proxy proxy = java.net.Proxy.NO_PROXY;
if (mHost != null) {
@@ -130,7 +165,10 @@
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- if (mHost != null) {
+ if (mPacFileUrl != null) {
+ sb.append("PAC Script: ");
+ sb.append(mPacFileUrl);
+ } else if (mHost != null) {
sb.append("[");
sb.append(mHost);
sb.append("] ");
@@ -148,6 +186,14 @@
public boolean equals(Object o) {
if (!(o instanceof ProxyProperties)) return false;
ProxyProperties p = (ProxyProperties)o;
+ // If PAC URL is present in either then they must be equal.
+ // Other parameters will only be for fall back.
+ if (!TextUtils.isEmpty(mPacFileUrl)) {
+ return mPacFileUrl.equals(p.getPacFileUrl()) && mPort == p.mPort;
+ }
+ if (!TextUtils.isEmpty(p.getPacFileUrl())) {
+ return false;
+ }
if (mExclusionList != null && !mExclusionList.equals(p.getExclusionList())) return false;
if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) {
return false;
@@ -181,6 +227,14 @@
* @hide
*/
public void writeToParcel(Parcel dest, int flags) {
+ if (mPacFileUrl != null) {
+ dest.writeByte((byte)1);
+ dest.writeString(mPacFileUrl);
+ dest.writeInt(mPort);
+ return;
+ } else {
+ dest.writeByte((byte)0);
+ }
if (mHost != null) {
dest.writeByte((byte)1);
dest.writeString(mHost);
@@ -201,7 +255,12 @@
public ProxyProperties createFromParcel(Parcel in) {
String host = null;
int port = 0;
- if (in.readByte() == 1) {
+ if (in.readByte() != 0) {
+ String url = in.readString();
+ int localPort = in.readInt();
+ return new ProxyProperties(url, localPort);
+ }
+ if (in.readByte() != 0) {
host = in.readString();
port = in.readInt();
}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index faae11e..6d23c32 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "NetUtils"
#include "jni.h"
+#include "JNIHelp.h"
#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
@@ -36,7 +37,8 @@
const char *server,
uint32_t *lease,
const char *vendorInfo,
- const char *domains);
+ const char *domains,
+ const char *mtu);
int dhcp_do_request_renew(const char * const ifname,
const char *ipaddr,
@@ -46,7 +48,8 @@
const char *server,
uint32_t *lease,
const char *vendorInfo,
- const char *domains);
+ const char *domains,
+ const char *mtu);
int dhcp_stop(const char *ifname);
int dhcp_release_lease(const char *ifname);
@@ -125,19 +128,20 @@
uint32_t lease;
char vendorInfo[PROPERTY_VALUE_MAX];
char domains[PROPERTY_VALUE_MAX];
+ char mtu[PROPERTY_VALUE_MAX];
const char *nameStr = env->GetStringUTFChars(ifname, NULL);
if (nameStr == NULL) return (jboolean)false;
if (renew) {
result = ::dhcp_do_request_renew(nameStr, ipaddr, gateway, &prefixLength,
- dns, server, &lease, vendorInfo, domains);
+ dns, server, &lease, vendorInfo, domains, mtu);
} else {
result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength,
- dns, server, &lease, vendorInfo, domains);
+ dns, server, &lease, vendorInfo, domains, mtu);
}
if (result != 0) {
- ALOGD("dhcp_do_request failed");
+ ALOGD("dhcp_do_request failed : %s (%s)", nameStr, renew ? "renew" : "new");
}
env->ReleaseStringUTFChars(ifname, nameStr);
@@ -239,6 +243,13 @@
return env->NewStringUTF(::dhcp_get_errmsg());
}
+static void android_net_utils_markSocket(JNIEnv *env, jobject thiz, jint socket, jint mark)
+{
+ if (setsockopt(socket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Error marking socket");
+ }
+}
+
// ----------------------------------------------------------------------------
/*
@@ -255,6 +266,7 @@
{ "stopDhcp", "(Ljava/lang/String;)Z", (void *)android_net_utils_stopDhcp },
{ "releaseDhcpLease", "(Ljava/lang/String;)Z", (void *)android_net_utils_releaseDhcpLease },
{ "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
+ { "markSocket", "(II)V", (void*) android_net_utils_markSocket },
};
int register_android_net_NetworkUtils(JNIEnv* env)
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index d6a7ee2..e63f6b0 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -25,13 +25,18 @@
import java.util.ArrayList;
public class LinkPropertiesTest extends TestCase {
- private static String ADDRV4 = "75.208.6.1";
- private static String ADDRV6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
- private static String DNS1 = "75.208.7.1";
- private static String DNS2 = "69.78.7.1";
- private static String GATEWAY1 = "75.208.8.1";
- private static String GATEWAY2 = "69.78.8.1";
+ private static InetAddress ADDRV4 = NetworkUtils.numericToInetAddress("75.208.6.1");
+ private static InetAddress ADDRV6 = NetworkUtils.numericToInetAddress(
+ "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 GATEWAY1 = NetworkUtils.numericToInetAddress("75.208.8.1");
+ private static InetAddress GATEWAY2 = NetworkUtils.numericToInetAddress("69.78.8.1");
private static String NAME = "qmi0";
+ private static int MTU = 1500;
+
+ private static LinkAddress LINKADDRV4 = new LinkAddress(ADDRV4, 32);
+ private static LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128);
public void assertLinkPropertiesEqual(LinkProperties source, LinkProperties target) {
// Check implementation of equals(), element by element.
@@ -53,6 +58,9 @@
assertTrue(source.isIdenticalStackedLinks(target));
assertTrue(target.isIdenticalStackedLinks(source));
+ assertTrue(source.isIdenticalMtu(target));
+ assertTrue(target.isIdenticalMtu(source));
+
// Check result of equals().
assertTrue(source.equals(target));
assertTrue(target.equals(source));
@@ -76,43 +84,40 @@
LinkProperties source = new LinkProperties();
source.setInterfaceName(NAME);
// set 2 link addresses
- source.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV4), 32));
- source.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV6), 128));
+ source.addLinkAddress(LINKADDRV4);
+ source.addLinkAddress(LINKADDRV6);
// set 2 dnses
- source.addDns(NetworkUtils.numericToInetAddress(DNS1));
- source.addDns(NetworkUtils.numericToInetAddress(DNS2));
+ source.addDns(DNS1);
+ source.addDns(DNS2);
// set 2 gateways
- source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
- source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+ source.addRoute(new RouteInfo(GATEWAY1));
+ source.addRoute(new RouteInfo(GATEWAY2));
+ source.setMtu(MTU);
LinkProperties target = new LinkProperties();
// All fields are same
target.setInterfaceName(NAME);
- target.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV4), 32));
- target.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV6), 128));
- target.addDns(NetworkUtils.numericToInetAddress(DNS1));
- target.addDns(NetworkUtils.numericToInetAddress(DNS2));
- target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
- target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+ target.addLinkAddress(LINKADDRV4);
+ target.addLinkAddress(LINKADDRV6);
+ target.addDns(DNS1);
+ target.addDns(DNS2);
+ target.addRoute(new RouteInfo(GATEWAY1));
+ target.addRoute(new RouteInfo(GATEWAY2));
+ target.setMtu(MTU);
assertLinkPropertiesEqual(source, target);
target.clear();
// change Interface Name
target.setInterfaceName("qmi1");
- target.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV4), 32));
- target.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV6), 128));
- target.addDns(NetworkUtils.numericToInetAddress(DNS1));
- target.addDns(NetworkUtils.numericToInetAddress(DNS2));
- target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
- target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+ target.addLinkAddress(LINKADDRV4);
+ target.addLinkAddress(LINKADDRV6);
+ target.addDns(DNS1);
+ target.addDns(DNS2);
+ target.addRoute(new RouteInfo(GATEWAY1));
+ target.addRoute(new RouteInfo(GATEWAY2));
+ target.setMtu(MTU);
assertFalse(source.equals(target));
target.clear();
@@ -120,38 +125,48 @@
// change link addresses
target.addLinkAddress(new LinkAddress(
NetworkUtils.numericToInetAddress("75.208.6.2"), 32));
- target.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV6), 128));
- target.addDns(NetworkUtils.numericToInetAddress(DNS1));
- target.addDns(NetworkUtils.numericToInetAddress(DNS2));
- target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
- target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+ target.addLinkAddress(LINKADDRV6);
+ target.addDns(DNS1);
+ target.addDns(DNS2);
+ target.addRoute(new RouteInfo(GATEWAY1));
+ target.addRoute(new RouteInfo(GATEWAY2));
+ target.setMtu(MTU);
assertFalse(source.equals(target));
target.clear();
target.setInterfaceName(NAME);
- target.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV4), 32));
- target.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV6), 128));
+ target.addLinkAddress(LINKADDRV4);
+ target.addLinkAddress(LINKADDRV6);
// change dnses
target.addDns(NetworkUtils.numericToInetAddress("75.208.7.2"));
- target.addDns(NetworkUtils.numericToInetAddress(DNS2));
- target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
- target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+ target.addDns(DNS2);
+ target.addRoute(new RouteInfo(GATEWAY1));
+ target.addRoute(new RouteInfo(GATEWAY2));
+ target.setMtu(MTU);
assertFalse(source.equals(target));
target.clear();
target.setInterfaceName(NAME);
- target.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV4), 32));
- target.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV6), 128));
- target.addDns(NetworkUtils.numericToInetAddress(DNS1));
- target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+ target.addLinkAddress(LINKADDRV4);
+ target.addLinkAddress(LINKADDRV6);
+ target.addDns(DNS1);
+ target.addDns(DNS2);
// change gateway
target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress("75.208.8.2")));
- target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+ target.addRoute(new RouteInfo(GATEWAY2));
+ target.setMtu(MTU);
+ assertFalse(source.equals(target));
+
+ target.clear();
+ target.setInterfaceName(NAME);
+ target.addLinkAddress(LINKADDRV4);
+ target.addLinkAddress(LINKADDRV6);
+ target.addDns(DNS1);
+ target.addDns(DNS2);
+ target.addRoute(new RouteInfo(GATEWAY1));
+ target.addRoute(new RouteInfo(GATEWAY2));
+ // change mtu
+ target.setMtu(1440);
assertFalse(source.equals(target));
} catch (Exception e) {
@@ -166,28 +181,26 @@
LinkProperties source = new LinkProperties();
source.setInterfaceName(NAME);
// set 2 link addresses
- source.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV4), 32));
- source.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV6), 128));
+ source.addLinkAddress(LINKADDRV4);
+ source.addLinkAddress(LINKADDRV6);
// set 2 dnses
- source.addDns(NetworkUtils.numericToInetAddress(DNS1));
- source.addDns(NetworkUtils.numericToInetAddress(DNS2));
+ source.addDns(DNS1);
+ source.addDns(DNS2);
// set 2 gateways
- source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
- source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+ source.addRoute(new RouteInfo(GATEWAY1));
+ source.addRoute(new RouteInfo(GATEWAY2));
+ source.setMtu(MTU);
LinkProperties target = new LinkProperties();
// Exchange order
target.setInterfaceName(NAME);
- target.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV6), 128));
- target.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV4), 32));
- target.addDns(NetworkUtils.numericToInetAddress(DNS2));
- target.addDns(NetworkUtils.numericToInetAddress(DNS1));
- target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
- target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+ target.addLinkAddress(LINKADDRV6);
+ target.addLinkAddress(LINKADDRV4);
+ target.addDns(DNS2);
+ target.addDns(DNS1);
+ target.addRoute(new RouteInfo(GATEWAY2));
+ target.addRoute(new RouteInfo(GATEWAY1));
+ target.setMtu(MTU);
assertLinkPropertiesEqual(source, target);
} catch (Exception e) {
@@ -200,21 +213,15 @@
try {
LinkProperties source = new LinkProperties();
// set 3 link addresses, eg, [A, A, B]
- source.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV4), 32));
- source.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV4), 32));
- source.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV6), 128));
+ source.addLinkAddress(LINKADDRV4);
+ source.addLinkAddress(LINKADDRV4);
+ source.addLinkAddress(LINKADDRV6);
LinkProperties target = new LinkProperties();
// set 3 link addresses, eg, [A, B, B]
- target.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV4), 32));
- target.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV6), 128));
- target.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress(ADDRV6), 128));
+ target.addLinkAddress(LINKADDRV4);
+ target.addLinkAddress(LINKADDRV6);
+ target.addLinkAddress(LINKADDRV6);
assertLinkPropertiesEqual(source, target);
} catch (Exception e) {
@@ -232,7 +239,7 @@
public void testRouteInterfaces() {
LinkAddress prefix = new LinkAddress(
NetworkUtils.numericToInetAddress("2001:db8::"), 32);
- InetAddress address = NetworkUtils.numericToInetAddress(ADDRV6);
+ InetAddress address = ADDRV6;
// Add a route with no interface to a LinkProperties with no interface. No errors.
LinkProperties lp = new LinkProperties();
@@ -265,7 +272,7 @@
assertAllRoutesHaveInterface("wlan0", lp);
// Routes with null interfaces are converted to wlan0.
- r = RouteInfo.makeHostRoute(NetworkUtils.numericToInetAddress(ADDRV6), null);
+ r = RouteInfo.makeHostRoute(ADDRV6, null);
lp.addRoute(r);
assertEquals(3, lp.getRoutes().size());
assertAllRoutesHaveInterface("wlan0", lp);
@@ -273,28 +280,45 @@
// Check comparisons work.
LinkProperties lp2 = new LinkProperties(lp);
assertAllRoutesHaveInterface("wlan0", lp);
- assertEquals(0, lp.compareRoutes(lp2).added.size());
- assertEquals(0, lp.compareRoutes(lp2).removed.size());
+ assertEquals(0, lp.compareAllRoutes(lp2).added.size());
+ assertEquals(0, lp.compareAllRoutes(lp2).removed.size());
lp2.setInterfaceName("p2p0");
assertAllRoutesHaveInterface("p2p0", lp2);
- assertEquals(3, lp.compareRoutes(lp2).added.size());
- assertEquals(3, lp.compareRoutes(lp2).removed.size());
+ assertEquals(3, lp.compareAllRoutes(lp2).added.size());
+ assertEquals(3, lp.compareAllRoutes(lp2).removed.size());
}
@SmallTest
public void testStackedInterfaces() {
LinkProperties rmnet0 = new LinkProperties();
rmnet0.setInterfaceName("rmnet0");
+ rmnet0.addLinkAddress(LINKADDRV6);
LinkProperties clat4 = new LinkProperties();
clat4.setInterfaceName("clat4");
+ clat4.addLinkAddress(LINKADDRV4);
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());
+
rmnet0.addStackedLink(clat4);
assertEquals(1, rmnet0.getStackedLinks().size());
+ assertEquals(1, rmnet0.getAddresses().size());
+ assertEquals(1, rmnet0.getLinkAddresses().size());
+ assertEquals(2, rmnet0.getAllAddresses().size());
+ assertEquals(2, rmnet0.getAllLinkAddresses().size());
+
rmnet0.addStackedLink(clat4);
assertEquals(1, rmnet0.getStackedLinks().size());
+ assertEquals(1, rmnet0.getAddresses().size());
+ assertEquals(1, rmnet0.getLinkAddresses().size());
+ assertEquals(2, rmnet0.getAllAddresses().size());
+ assertEquals(2, rmnet0.getAllLinkAddresses().size());
+
assertEquals(0, clat4.getStackedLinks().size());
// Modify an item in the returned collection to see what happens.
@@ -306,5 +330,76 @@
for (LinkProperties link : rmnet0.getStackedLinks()) {
assertFalse("newname".equals(link.getInterfaceName()));
}
+
+ 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));
+ }
+
+ @SmallTest
+ public void testAddressMethods() {
+ LinkProperties lp = new LinkProperties();
+
+ // No addresses.
+ assertFalse(lp.hasIPv4Address());
+ assertFalse(lp.hasIPv6Address());
+
+ // Addresses on stacked links don't count.
+ LinkProperties stacked = new LinkProperties();
+ stacked.setInterfaceName("stacked");
+ lp.addStackedLink(stacked);
+ stacked.addLinkAddress(LINKADDRV4);
+ stacked.addLinkAddress(LINKADDRV6);
+ assertTrue(stacked.hasIPv4Address());
+ assertTrue(stacked.hasIPv6Address());
+ assertFalse(lp.hasIPv4Address());
+ assertFalse(lp.hasIPv6Address());
+ lp.removeStackedLink(stacked);
+ assertFalse(lp.hasIPv4Address());
+ assertFalse(lp.hasIPv6Address());
+
+ // Addresses on the base link.
+ // Check the return values of hasIPvXAddress and ensure the add/remove methods return true
+ // iff something changes.
+ assertTrue(lp.addLinkAddress(LINKADDRV6));
+ assertFalse(lp.hasIPv4Address());
+ assertTrue(lp.hasIPv6Address());
+
+ assertTrue(lp.removeLinkAddress(LINKADDRV6));
+ assertTrue(lp.addLinkAddress(LINKADDRV4));
+ assertTrue(lp.hasIPv4Address());
+ assertFalse(lp.hasIPv6Address());
+
+ assertTrue(lp.addLinkAddress(LINKADDRV6));
+ assertTrue(lp.hasIPv4Address());
+ assertTrue(lp.hasIPv6Address());
+
+ // Adding an address twice has no effect.
+ // Removing an address that's not present has no effect.
+ assertFalse(lp.addLinkAddress(LINKADDRV4));
+ assertTrue(lp.hasIPv4Address());
+ assertTrue(lp.removeLinkAddress(LINKADDRV4));
+ assertFalse(lp.hasIPv4Address());
+ assertFalse(lp.removeLinkAddress(LINKADDRV4));
+ }
+
+ @SmallTest
+ public void testSetLinkAddresses() {
+ LinkProperties lp = new LinkProperties();
+ lp.addLinkAddress(LINKADDRV4);
+ lp.addLinkAddress(LINKADDRV6);
+
+ LinkProperties lp2 = new LinkProperties();
+ lp2.addLinkAddress(LINKADDRV6);
+
+ assertFalse(lp.equals(lp2));
+
+ lp2.setLinkAddresses(lp.getLinkAddresses());
+ assertTrue(lp.equals(lp));
}
}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index a022fb1..baff661 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -31,6 +31,7 @@
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -57,13 +58,12 @@
import android.net.INetworkStatsService;
import android.net.LinkAddress;
import android.net.LinkProperties;
-import android.net.Uri;
import android.net.LinkProperties.CompareResult;
+import android.net.LinkQualityInfo;
import android.net.MobileDataStateTracker;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkInfo.State;
import android.net.NetworkQuotaInfo;
import android.net.NetworkState;
import android.net.NetworkStateTracker;
@@ -71,10 +71,13 @@
import android.net.Proxy;
import android.net.ProxyProperties;
import android.net.RouteInfo;
+import android.net.SamplingDataTracker;
+import android.net.Uri;
import android.net.wifi.WifiStateTracker;
import android.net.wimax.WimaxManagerConstants;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.Build;
import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
@@ -87,7 +90,6 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -98,10 +100,12 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.Xml;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
@@ -111,7 +115,9 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
+import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.Nat464Xlat;
+import com.android.server.connectivity.PacManager;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.BaseNetworkObserver;
@@ -136,17 +142,24 @@
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.URL;
+import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.GregorianCalendar;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
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
*/
@@ -172,6 +185,23 @@
private static final String FAIL_FAST_TIME_MS =
"persist.radio.fail_fast_time_ms";
+ private static final String ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED =
+ "android.net.ConnectivityService.action.PKT_CNT_SAMPLE_INTERVAL_ELAPSED";
+
+ private static final int SAMPLE_INTERVAL_ELAPSED_REQUEST_CODE = 0;
+
+ private PendingIntent mSampleIntervalElapsedIntent;
+
+ // Set network sampling interval at 12 minutes, this way, even if the timers get
+ // aggregated, it will fire at around 15 minutes, which should allow us to
+ // aggregate this timer with other timers (specially the socket keep alive timers)
+ private static final int DEFAULT_SAMPLING_INTERVAL_IN_SECONDS = (VDBG ? 30 : 12 * 60);
+
+ // start network sampling a minute after booting ...
+ private static final int DEFAULT_START_SAMPLING_INTERVAL_IN_SECONDS = (VDBG ? 30 : 60);
+
+ AlarmManager mAlarmManager;
+
// used in recursive route setting to add gateways for the host for which
// a host route was requested.
private static final int MAX_HOSTROUTE_CYCLE_COUNT = 10;
@@ -180,7 +210,8 @@
private KeyStore mKeyStore;
- private Vpn mVpn;
+ @GuardedBy("mVpns")
+ private final SparseArray<Vpn> mVpns = new SparseArray<Vpn>();
private VpnCallback mVpnCallback = new VpnCallback();
private boolean mLockdownEnabled;
@@ -232,7 +263,6 @@
private Object mDnsLock = new Object();
private int mNumDnsEntries;
- private boolean mDnsOverridden = false;
private boolean mTestMode;
private static ConnectivityService sServiceInstance;
@@ -249,6 +279,9 @@
private static final boolean TO_DEFAULT_TABLE = true;
private static final boolean TO_SECONDARY_TABLE = false;
+ private static final boolean EXEMPT = true;
+ private static final boolean UNEXEMPT = false;
+
/**
* used internally as a delayed event to make us switch back to the
* default network
@@ -304,28 +337,32 @@
private static final int EVENT_SET_DEPENDENCY_MET = 10;
/**
- * used internally to restore DNS properties back to the
- * default network
- */
- private static final int EVENT_RESTORE_DNS = 11;
-
- /**
* used internally to send a sticky broadcast delayed.
*/
- private static final int EVENT_SEND_STICKY_BROADCAST_INTENT = 12;
+ 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 = 13;
+ private static final int EVENT_SET_POLICY_DATA_ENABLE = 12;
- private static final int EVENT_VPN_STATE_CHANGED = 14;
+ private static final int EVENT_VPN_STATE_CHANGED = 13;
/**
* Used internally to disable fail fast of mobile data
*/
- private static final int EVENT_ENABLE_FAIL_FAST_MOBILE_DATA = 15;
+ private static final int EVENT_ENABLE_FAIL_FAST_MOBILE_DATA = 14;
+
+ /**
+ * user internally to indicate that data sampling interval is up
+ */
+ private static final int EVENT_SAMPLE_INTERVAL_ELAPSED = 15;
+
+ /**
+ * PAC manager has received new port.
+ */
+ private static final int EVENT_PROXY_HAS_CHANGED = 16;
/** Handler used for internal events. */
private InternalHandler mHandler;
@@ -346,10 +383,19 @@
private InetAddress mDefaultDns;
+ // Lock for protecting access to mAddedRoutes and mExemptAddresses
+ private final Object mRoutesLock = new Object();
+
// this collection is used to refcount the added routes - if there are none left
// it's time to remove the route from the route table
+ @GuardedBy("mRoutesLock")
private Collection<RouteInfo> mAddedRoutes = new ArrayList<RouteInfo>();
+ // this collection corresponds to the entries of mAddedRoutes that have routing exemptions
+ // used to handle cleanup of exempt rules
+ @GuardedBy("mRoutesLock")
+ private Collection<LinkAddress> mExemptAddresses = new ArrayList<LinkAddress>();
+
// used in DBG mode to track inet condition reports
private static final int INET_CONDITION_LOG_MAX_SIZE = 15;
private ArrayList mInetLog;
@@ -362,6 +408,8 @@
// track the global proxy.
private ProxyProperties mGlobalProxy = null;
+ private PacManager mPacManager = null;
+
private SettingsObserver mSettingsObserver;
NetworkConfig[] mNetConfigs;
@@ -381,6 +429,8 @@
// the set of network types that can only be enabled by system/sig apps
List mProtectedNetworks;
+ private DataConnectionStats mDataConnectionStats;
+
private AtomicInteger mEnableFailFastMobileDataTag = new AtomicInteger(0);
TelephonyManager mTelephonyManager;
@@ -460,6 +510,7 @@
com.android.internal.R.array.radioAttributes);
for (String raString : raStrings) {
RadioAttributes r = new RadioAttributes(raString);
+ if (VDBG) log("raString=" + raString + " r=" + r);
if (r.mType > ConnectivityManager.MAX_RADIO_TYPE) {
loge("Error in radioAttributes - ignoring attempt to define type " + r.mType);
continue;
@@ -480,6 +531,7 @@
for (String naString : naStrings) {
try {
NetworkConfig n = new NetworkConfig(naString);
+ if (VDBG) log("naString=" + naString + " config=" + n);
if (n.type > ConnectivityManager.MAX_NETWORK_TYPE) {
loge("Error in networkAttributes - ignoring attempt to define type " +
n.type);
@@ -506,6 +558,7 @@
// ignore it - leave the entry null
}
}
+ if (VDBG) log("mNetworksDefined=" + mNetworksDefined);
mProtectedNetworks = new ArrayList<Integer>();
int[] protectedNetworks = context.getResources().getIntArray(
@@ -588,9 +641,12 @@
mTethering = new Tethering(mContext, mNetd, statsService, this, mHandler.getLooper());
- mVpn = new Vpn(mContext, mVpnCallback, mNetd, this);
- mVpn.startMonitoring(mContext, mTrackerHandler);
-
+ //set up the listener for user state for creating user VPNs
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_USER_STARTING);
+ intentFilter.addAction(Intent.ACTION_USER_STOPPING);
+ mContext.registerReceiverAsUser(
+ mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
mClat = new Nat464Xlat(mContext, mNetd, this, mTrackerHandler);
try {
@@ -608,7 +664,35 @@
mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
mSettingsObserver.observe(mContext);
+ mDataConnectionStats = new DataConnectionStats(mContext);
+ mDataConnectionStats.startMonitoring();
+
+ // start network sampling ..
+ Intent intent = new Intent(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED, null);
+ mSampleIntervalElapsedIntent = PendingIntent.getBroadcast(mContext,
+ SAMPLE_INTERVAL_ELAPSED_REQUEST_CODE, intent, 0);
+
+ mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+ setAlarm(DEFAULT_START_SAMPLING_INTERVAL_IN_SECONDS * 1000, mSampleIntervalElapsedIntent);
+
IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED);
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED)) {
+ mHandler.sendMessage(mHandler.obtainMessage
+ (EVENT_SAMPLE_INTERVAL_ELAPSED));
+ }
+ }
+ },
+ new IntentFilter(filter));
+
+ mPacManager = new PacManager(mContext, mHandler, EVENT_PROXY_HAS_CHANGED);
+
+ filter = new IntentFilter();
filter.addAction(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
mContext.registerReceiver(mProvisioningReceiver, filter);
}
@@ -1502,7 +1586,7 @@
try {
InetAddress addr = InetAddress.getByAddress(hostAddress);
LinkProperties lp = tracker.getLinkProperties();
- boolean ok = addRouteToAddress(lp, addr);
+ boolean ok = addRouteToAddress(lp, addr, EXEMPT);
if (DBG) log("requestRouteToHostAddress ok=" + ok);
return ok;
} catch (UnknownHostException e) {
@@ -1514,24 +1598,25 @@
return false;
}
- private boolean addRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable) {
- return modifyRoute(p, r, 0, ADD, toDefaultTable);
+ private boolean addRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable,
+ boolean exempt) {
+ return modifyRoute(p, r, 0, ADD, toDefaultTable, exempt);
}
private boolean removeRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable) {
- return modifyRoute(p, r, 0, REMOVE, toDefaultTable);
+ return modifyRoute(p, r, 0, REMOVE, toDefaultTable, UNEXEMPT);
}
- private boolean addRouteToAddress(LinkProperties lp, InetAddress addr) {
- return modifyRouteToAddress(lp, addr, ADD, TO_DEFAULT_TABLE);
+ private boolean addRouteToAddress(LinkProperties lp, InetAddress addr, boolean exempt) {
+ return modifyRouteToAddress(lp, addr, ADD, TO_DEFAULT_TABLE, exempt);
}
private boolean removeRouteToAddress(LinkProperties lp, InetAddress addr) {
- return modifyRouteToAddress(lp, addr, REMOVE, TO_DEFAULT_TABLE);
+ return modifyRouteToAddress(lp, addr, REMOVE, TO_DEFAULT_TABLE, UNEXEMPT);
}
private boolean modifyRouteToAddress(LinkProperties lp, InetAddress addr, boolean doAdd,
- boolean toDefaultTable) {
+ boolean toDefaultTable, boolean exempt) {
RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getAllRoutes(), addr);
if (bestRoute == null) {
bestRoute = RouteInfo.makeHostRoute(addr, lp.getInterfaceName());
@@ -1546,11 +1631,11 @@
bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway(), iface);
}
}
- return modifyRoute(lp, bestRoute, 0, doAdd, toDefaultTable);
+ return modifyRoute(lp, bestRoute, 0, doAdd, toDefaultTable, exempt);
}
private boolean modifyRoute(LinkProperties lp, RouteInfo r, int cycleCount, boolean doAdd,
- boolean toDefaultTable) {
+ boolean toDefaultTable, boolean exempt) {
if ((lp == null) || (r == null)) {
if (DBG) log("modifyRoute got unexpected null: " + lp + ", " + r);
return false;
@@ -1579,15 +1664,25 @@
bestRoute.getGateway(),
ifaceName);
}
- modifyRoute(lp, bestRoute, cycleCount+1, doAdd, toDefaultTable);
+ modifyRoute(lp, bestRoute, cycleCount+1, doAdd, toDefaultTable, exempt);
}
}
if (doAdd) {
if (VDBG) log("Adding " + r + " for interface " + ifaceName);
try {
if (toDefaultTable) {
- mAddedRoutes.add(r); // only track default table - only one apps can effect
- mNetd.addRoute(ifaceName, r);
+ synchronized (mRoutesLock) {
+ // only track default table - only one apps can effect
+ mAddedRoutes.add(r);
+ mNetd.addRoute(ifaceName, r);
+ if (exempt) {
+ LinkAddress dest = r.getDestination();
+ if (!mExemptAddresses.contains(dest)) {
+ mNetd.setHostExemption(dest);
+ mExemptAddresses.add(dest);
+ }
+ }
+ }
} else {
mNetd.addSecondaryRoute(ifaceName, r);
}
@@ -1600,18 +1695,25 @@
// if we remove this one and there are no more like it, then refcount==0 and
// we can remove it from the table
if (toDefaultTable) {
- mAddedRoutes.remove(r);
- if (mAddedRoutes.contains(r) == false) {
- if (VDBG) log("Removing " + r + " for interface " + ifaceName);
- try {
- mNetd.removeRoute(ifaceName, r);
- } catch (Exception e) {
- // never crash - catch them all
- if (VDBG) loge("Exception trying to remove a route: " + e);
- return false;
+ synchronized (mRoutesLock) {
+ mAddedRoutes.remove(r);
+ if (mAddedRoutes.contains(r) == false) {
+ if (VDBG) log("Removing " + r + " for interface " + ifaceName);
+ try {
+ mNetd.removeRoute(ifaceName, r);
+ LinkAddress dest = r.getDestination();
+ if (mExemptAddresses.contains(dest)) {
+ mNetd.clearHostExemption(dest);
+ mExemptAddresses.remove(dest);
+ }
+ } catch (Exception e) {
+ // never crash - catch them all
+ if (VDBG) loge("Exception trying to remove a route: " + e);
+ return false;
+ }
+ } else {
+ if (VDBG) log("not removing " + r + " as it's still in use");
}
- } else {
- if (VDBG) log("not removing " + r + " as it's still in use");
}
} else {
if (VDBG) log("Removing " + r + " for interface " + ifaceName);
@@ -1788,6 +1890,16 @@
"ConnectivityService");
}
+ private void enforceMarkNetworkSocketPermission() {
+ //Media server special case
+ if (Binder.getCallingUid() == Process.MEDIA_UID) {
+ return;
+ }
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MARK_NETWORK_SOCKET,
+ "ConnectivityService");
+ }
+
/**
* Handle a {@code DISCONNECTED} event. If this pertains to the non-active
* network, we ignore it. If it is for the active network, we send out a
@@ -1906,6 +2018,7 @@
// if (mActiveDefaultNetwork != -1) {
// currentPriority = mNetConfigs[mActiveDefaultNetwork].mPriority;
// }
+
for (int checkType=0; checkType <= ConnectivityManager.MAX_NETWORK_TYPE; checkType++) {
if (checkType == prevNetType) continue;
if (mNetConfigs[checkType] == null) continue;
@@ -1920,6 +2033,7 @@
// optimization should work and we need to investigate why it doesn't work.
// This could be related to how DEACTIVATE_DATA_CALL is reporting its
// complete before it is really complete.
+
// if (!mNetTrackers[checkType].isAvailable()) continue;
// if (currentPriority >= mNetConfigs[checkType].mPriority) continue;
@@ -2194,6 +2308,7 @@
}
thisNet.setTeardownRequested(false);
updateNetworkSettings(thisNet);
+ updateMtuSizeSettings(thisNet);
handleConnectivityChange(newNetType, false);
sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
@@ -2305,6 +2420,7 @@
*/
private void handleConnectivityChange(int netType, boolean doReset) {
int resetMask = doReset ? NetworkUtils.RESET_ALL_ADDRESSES : 0;
+ boolean exempt = ConnectivityManager.isNetworkTypeExempt(netType);
if (VDBG) {
log("handleConnectivityChange: netType=" + netType + " doReset=" + doReset
+ " resetMask=" + resetMask);
@@ -2373,7 +2489,7 @@
}
}
mCurrentLinkProperties[netType] = newLp;
- boolean resetDns = updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault());
+ boolean resetDns = updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault(), exempt);
if (resetMask != 0 || resetDns) {
if (VDBG) log("handleConnectivityChange: resetting");
@@ -2388,7 +2504,11 @@
// Tell VPN the interface is down. It is a temporary
// but effective fix to make VPN aware of the change.
if ((resetMask & NetworkUtils.RESET_IPV4_ADDRESSES) != 0) {
- mVpn.interfaceStatusChanged(iface, false);
+ synchronized(mVpns) {
+ for (int i = 0; i < mVpns.size(); i++) {
+ mVpns.valueAt(i).interfaceStatusChanged(iface, false);
+ }
+ }
}
}
if (resetDns) {
@@ -2448,13 +2568,13 @@
* returns a boolean indicating the routes changed
*/
private boolean updateRoutes(LinkProperties newLp, LinkProperties curLp,
- boolean isLinkDefault) {
+ boolean isLinkDefault, boolean exempt) {
Collection<RouteInfo> routesToAdd = null;
CompareResult<InetAddress> dnsDiff = new CompareResult<InetAddress>();
CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>();
if (curLp != null) {
// check for the delta between the current set and the new
- routeDiff = curLp.compareRoutes(newLp);
+ routeDiff = curLp.compareAllRoutes(newLp);
dnsDiff = curLp.compareDnses(newLp);
} else if (newLp != null) {
routeDiff.added = newLp.getAllRoutes();
@@ -2485,7 +2605,7 @@
}
if (newLp != null) {
for (InetAddress newDns : newLp.getDnses()) {
- addRouteToAddress(newLp, newDns);
+ addRouteToAddress(newLp, newDns, exempt);
}
}
} else {
@@ -2494,27 +2614,30 @@
removeRouteToAddress(curLp, oldDns);
}
for (InetAddress newDns : dnsDiff.added) {
- addRouteToAddress(newLp, newDns);
+ addRouteToAddress(newLp, newDns, exempt);
}
}
}
for (RouteInfo r : routeDiff.added) {
if (isLinkDefault || ! r.isDefaultRoute()) {
- addRoute(newLp, r, TO_DEFAULT_TABLE);
+ addRoute(newLp, r, TO_DEFAULT_TABLE, exempt);
} else {
// add to a secondary route table
- addRoute(newLp, r, TO_SECONDARY_TABLE);
+ addRoute(newLp, r, TO_SECONDARY_TABLE, UNEXEMPT);
// many radios add a default route even when we don't want one.
// remove the default route unless somebody else has asked for it
String ifaceName = newLp.getInterfaceName();
- if (TextUtils.isEmpty(ifaceName) == false && mAddedRoutes.contains(r) == false) {
- try {
- mNetd.removeRoute(ifaceName, r);
- } catch (Exception e) {
- // never crash - catch them all
- if (DBG) loge("Exception trying to remove a route: " + e);
+ synchronized (mRoutesLock) {
+ if (!TextUtils.isEmpty(ifaceName) && !mAddedRoutes.contains(r)) {
+ if (VDBG) log("Removing " + r + " for interface " + ifaceName);
+ try {
+ mNetd.removeRoute(ifaceName, r);
+ } catch (Exception e) {
+ // never crash - catch them all
+ if (DBG) loge("Exception trying to remove a route: " + e);
+ }
}
}
}
@@ -2523,13 +2646,33 @@
return routesChanged;
}
-
/**
+ * Reads the network specific MTU size from reources.
+ * and set it on it's iface.
+ */
+ private void updateMtuSizeSettings(NetworkStateTracker nt) {
+ final String iface = nt.getLinkProperties().getInterfaceName();
+ final int mtu = nt.getLinkProperties().getMtu();
+
+ if (mtu < 68 || mtu > 10000) {
+ loge("Unexpected mtu value: " + nt);
+ return;
+ }
+
+ try {
+ if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
+ mNetd.setMtu(iface, mtu);
+ } catch (Exception e) {
+ Slog.e(TAG, "exception in setMtu()" + e);
+ }
+ }
+
+ /**
* Reads the network specific TCP buffer sizes from SystemProperties
* net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system
* wide use
*/
- private void updateNetworkSettings(NetworkStateTracker nt) {
+ private void updateNetworkSettings(NetworkStateTracker nt) {
String key = nt.getTcpBufferSizesPropName();
String bufferSizes = key == null ? null : SystemProperties.get(key);
@@ -2551,7 +2694,7 @@
}
}
- /**
+ /**
* Writes TCP buffer sizes to /sys/kernel/ipv4/tcp_[r/w]mem_[min/def/max]
* which maps to /proc/sys/net/ipv4/tcp_rmem and tcpwmem
*
@@ -2634,7 +2777,7 @@
// Caller must grab mDnsLock.
private void updateDnsLocked(String network, String iface,
- Collection<InetAddress> dnses, String domains) {
+ Collection<InetAddress> dnses, String domains, boolean defaultDns) {
int last = 0;
if (dnses.size() == 0 && mDefaultDns != null) {
dnses = new ArrayList();
@@ -2646,7 +2789,10 @@
try {
mNetd.setDnsServersForInterface(iface, NetworkUtils.makeStrings(dnses), domains);
- mNetd.setDefaultInterfaceForDns(iface);
+ if (defaultDns) {
+ mNetd.setDefaultInterfaceForDns(iface);
+ }
+
for (InetAddress dns : dnses) {
++last;
String key = "net.dns" + last;
@@ -2659,7 +2805,7 @@
}
mNumDnsEntries = last;
} catch (Exception e) {
- if (DBG) loge("exception setting default dns interface: " + e);
+ loge("exception setting default dns interface: " + e);
}
}
@@ -2673,9 +2819,7 @@
if (mNetConfigs[netType].isDefault()) {
String network = nt.getNetworkInfo().getTypeName();
synchronized (mDnsLock) {
- if (!mDnsOverridden) {
- updateDnsLocked(network, p.getInterfaceName(), dnses, p.getDomains());
- }
+ updateDnsLocked(network, p.getInterfaceName(), dnses, p.getDomains(), true);
}
} else {
try {
@@ -2820,8 +2964,11 @@
if (ConnectivityManager.isNetworkTypeMobile(info.getType())
&& (0 != Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 0))
- && ((state == NetworkInfo.State.CONNECTED)
- || info.isConnectedToProvisioningNetwork())) {
+ && (((state == NetworkInfo.State.CONNECTED)
+ && (info.getType() == ConnectivityManager.TYPE_MOBILE))
+ || info.isConnectedToProvisioningNetwork())) {
+ log("ConnectivityChange checkMobileProvisioning for"
+ + " TYPE_MOBILE or ProvisioningNetwork");
checkMobileProvisioning(CheckMp.MAX_TIMEOUT_MS);
}
@@ -2903,7 +3050,7 @@
public void handleMessage(Message msg) {
NetworkInfo info;
switch (msg.what) {
- case EVENT_CLEAR_NET_TRANSITION_WAKELOCK:
+ case EVENT_CLEAR_NET_TRANSITION_WAKELOCK: {
String causedBy = null;
synchronized (ConnectivityService.this) {
if (msg.arg1 == mNetTransitionWakeLockSerialNumber &&
@@ -2916,56 +3063,44 @@
log("NetTransition Wakelock for " + causedBy + " released by timeout");
}
break;
- case EVENT_RESTORE_DEFAULT_NETWORK:
+ }
+ case EVENT_RESTORE_DEFAULT_NETWORK: {
FeatureUser u = (FeatureUser)msg.obj;
u.expire();
break;
- case EVENT_INET_CONDITION_CHANGE:
- {
+ }
+ case EVENT_INET_CONDITION_CHANGE: {
int netType = msg.arg1;
int condition = msg.arg2;
handleInetConditionChange(netType, condition);
break;
}
- case EVENT_INET_CONDITION_HOLD_END:
- {
+ case EVENT_INET_CONDITION_HOLD_END: {
int netType = msg.arg1;
int sequence = msg.arg2;
handleInetConditionHoldEnd(netType, sequence);
break;
}
- case EVENT_SET_NETWORK_PREFERENCE:
- {
+ case EVENT_SET_NETWORK_PREFERENCE: {
int preference = msg.arg1;
handleSetNetworkPreference(preference);
break;
}
- case EVENT_SET_MOBILE_DATA:
- {
+ case EVENT_SET_MOBILE_DATA: {
boolean enabled = (msg.arg1 == ENABLED);
handleSetMobileData(enabled);
break;
}
- case EVENT_APPLY_GLOBAL_HTTP_PROXY:
- {
+ case EVENT_APPLY_GLOBAL_HTTP_PROXY: {
handleDeprecatedGlobalHttpProxy();
break;
}
- case EVENT_SET_DEPENDENCY_MET:
- {
+ case EVENT_SET_DEPENDENCY_MET: {
boolean met = (msg.arg1 == ENABLED);
handleSetDependencyMet(msg.arg2, met);
break;
}
- case EVENT_RESTORE_DNS:
- {
- if (mActiveDefaultNetwork != -1) {
- handleDnsConfigurationChange(mActiveDefaultNetwork);
- }
- break;
- }
- case EVENT_SEND_STICKY_BROADCAST_INTENT:
- {
+ case EVENT_SEND_STICKY_BROADCAST_INTENT: {
Intent intent = (Intent)msg.obj;
sendStickyBroadcast(intent);
break;
@@ -2994,6 +3129,15 @@
log("EVENT_ENABLE_FAIL_FAST_MOBILE_DATA: stale arg1:" + msg.arg1
+ " != tag:" + tag);
}
+ break;
+ }
+ case EVENT_SAMPLE_INTERVAL_ELAPSED: {
+ handleNetworkSamplingTimeout();
+ break;
+ }
+ case EVENT_PROXY_HAS_CHANGED: {
+ handleApplyDefaultProxy((ProxyProperties)msg.obj);
+ break;
}
}
}
@@ -3081,12 +3225,6 @@
return mTethering.getTetheredIfaces();
}
- @Override
- public String[] getTetheredIfacePairs() {
- enforceTetherAccessPermission();
- return mTethering.getTetheredIfacePairs();
- }
-
public String[] getTetheringErroredIfaces() {
enforceTetherAccessPermission();
return mTethering.getErroredIfaces();
@@ -3222,13 +3360,15 @@
// of proxy info to all the JVMs.
// enforceAccessPermission();
synchronized (mProxyLock) {
- if (mGlobalProxy != null) return mGlobalProxy;
- return (mDefaultProxyDisabled ? null : mDefaultProxy);
+ ProxyProperties ret = mGlobalProxy;
+ if ((ret == null) && !mDefaultProxyDisabled) ret = mDefaultProxy;
+ return ret;
}
}
public void setGlobalProxy(ProxyProperties proxyProperties) {
enforceConnectivityInternalPermission();
+
synchronized (mProxyLock) {
if (proxyProperties == mGlobalProxy) return;
if (proxyProperties != null && proxyProperties.equals(mGlobalProxy)) return;
@@ -3237,11 +3377,21 @@
String host = "";
int port = 0;
String exclList = "";
- if (proxyProperties != null && !TextUtils.isEmpty(proxyProperties.getHost())) {
+ String pacFileUrl = "";
+ if (proxyProperties != null && (!TextUtils.isEmpty(proxyProperties.getHost()) ||
+ !TextUtils.isEmpty(proxyProperties.getPacFileUrl()))) {
+ if (!proxyProperties.isValid()) {
+ if (DBG)
+ log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
+ return;
+ }
mGlobalProxy = new ProxyProperties(proxyProperties);
host = mGlobalProxy.getHost();
port = mGlobalProxy.getPort();
exclList = mGlobalProxy.getExclusionList();
+ if (proxyProperties.getPacFileUrl() != null) {
+ pacFileUrl = proxyProperties.getPacFileUrl();
+ }
} else {
mGlobalProxy = null;
}
@@ -3252,6 +3402,7 @@
Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, port);
Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
exclList);
+ Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC, pacFileUrl);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -3269,8 +3420,19 @@
int port = Settings.Global.getInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, 0);
String exclList = Settings.Global.getString(res,
Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
- if (!TextUtils.isEmpty(host)) {
- ProxyProperties proxyProperties = new ProxyProperties(host, port, exclList);
+ String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC);
+ if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
+ ProxyProperties proxyProperties;
+ if (!TextUtils.isEmpty(pacFileUrl)) {
+ proxyProperties = new ProxyProperties(pacFileUrl);
+ } else {
+ proxyProperties = new ProxyProperties(host, port, exclList);
+ }
+ if (!proxyProperties.isValid()) {
+ if (DBG) log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
+ return;
+ }
+
synchronized (mProxyLock) {
mGlobalProxy = proxyProperties;
}
@@ -3288,12 +3450,17 @@
}
private void handleApplyDefaultProxy(ProxyProperties proxy) {
- if (proxy != null && TextUtils.isEmpty(proxy.getHost())) {
+ if (proxy != null && TextUtils.isEmpty(proxy.getHost())
+ && TextUtils.isEmpty(proxy.getPacFileUrl())) {
proxy = null;
}
synchronized (mProxyLock) {
if (mDefaultProxy != null && mDefaultProxy.equals(proxy)) return;
if (mDefaultProxy == proxy) return; // catches repeated nulls
+ if (proxy != null && !proxy.isValid()) {
+ if (DBG) log("Invalid proxy properties, ignoring: " + proxy.toString());
+ return;
+ }
mDefaultProxy = proxy;
if (mGlobalProxy != null) return;
@@ -3308,6 +3475,10 @@
Settings.Global.HTTP_PROXY);
if (!TextUtils.isEmpty(proxy)) {
String data[] = proxy.split(":");
+ if (data.length == 0) {
+ return;
+ }
+
String proxyHost = data[0];
int proxyPort = 8080;
if (data.length > 1) {
@@ -3324,6 +3495,7 @@
private void sendProxyBroadcast(ProxyProperties proxy) {
if (proxy == null) proxy = new ProxyProperties("", 0, "");
+ if (mPacManager.setCurrentProxyScriptUrl(proxy)) return;
if (DBG) log("sending Proxy Broadcast for " + proxy);
Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
@@ -3418,8 +3590,12 @@
throwIfLockdownEnabled();
try {
int type = mActiveDefaultNetwork;
+ int user = UserHandle.getUserId(Binder.getCallingUid());
if (ConnectivityManager.isNetworkTypeValid(type) && mNetTrackers[type] != null) {
- mVpn.protect(socket, mNetTrackers[type].getLinkProperties().getInterfaceName());
+ synchronized(mVpns) {
+ mVpns.get(user).protect(socket,
+ mNetTrackers[type].getLinkProperties().getInterfaceName());
+ }
return true;
}
} catch (Exception e) {
@@ -3443,7 +3619,27 @@
@Override
public boolean prepareVpn(String oldPackage, String newPackage) {
throwIfLockdownEnabled();
- return mVpn.prepare(oldPackage, newPackage);
+ int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized(mVpns) {
+ return mVpns.get(user).prepare(oldPackage, newPackage);
+ }
+ }
+
+ @Override
+ public void markSocketAsUser(ParcelFileDescriptor socket, int uid) {
+ enforceMarkNetworkSocketPermission();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ int mark = mNetd.getMarkForUid(uid);
+ // Clear the mark on the socket if no mark is needed to prevent socket reuse issues
+ if (mark == -1) {
+ mark = 0;
+ }
+ NetworkUtils.markSocket(socket.getFd(), mark);
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
@@ -3456,7 +3652,10 @@
@Override
public ParcelFileDescriptor establishVpn(VpnConfig config) {
throwIfLockdownEnabled();
- return mVpn.establish(config);
+ int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized(mVpns) {
+ return mVpns.get(user).establish(config);
+ }
}
/**
@@ -3470,7 +3669,10 @@
if (egress == null) {
throw new IllegalStateException("Missing active network connection");
}
- mVpn.startLegacyVpn(profile, mKeyStore, egress);
+ int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized(mVpns) {
+ mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress);
+ }
}
/**
@@ -3482,7 +3684,24 @@
@Override
public LegacyVpnInfo getLegacyVpnInfo() {
throwIfLockdownEnabled();
- return mVpn.getLegacyVpnInfo();
+ int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized(mVpns) {
+ return mVpns.get(user).getLegacyVpnInfo();
+ }
+ }
+
+ /**
+ * Returns the information of the ongoing VPN. This method is used by VpnDialogs and
+ * not available in ConnectivityManager.
+ * Permissions are checked in Vpn class.
+ * @hide
+ */
+ @Override
+ public VpnConfig getVpnConfig() {
+ int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized(mVpns) {
+ return mVpns.get(user).getVpnConfig();
+ }
}
/**
@@ -3503,7 +3722,7 @@
mHandler.obtainMessage(EVENT_VPN_STATE_CHANGED, info).sendToTarget();
}
- public void override(List<String> dnsServers, List<String> searchDomains) {
+ public void override(String iface, List<String> dnsServers, List<String> searchDomains) {
if (dnsServers == null) {
restore();
return;
@@ -3535,8 +3754,7 @@
// Apply DNS changes.
synchronized (mDnsLock) {
- updateDnsLocked("VPN", "VPN", addresses, domains);
- mDnsOverridden = true;
+ updateDnsLocked("VPN", iface, addresses, domains, false);
}
// Temporarily disable the default proxy (not global).
@@ -3551,12 +3769,6 @@
}
public void restore() {
- synchronized (mDnsLock) {
- if (mDnsOverridden) {
- mDnsOverridden = false;
- mHandler.sendEmptyMessage(EVENT_RESTORE_DNS);
- }
- }
synchronized (mProxyLock) {
mDefaultProxyDisabled = false;
if (mGlobalProxy == null && mDefaultProxy != null) {
@@ -3564,6 +3776,69 @@
}
}
}
+
+ public void protect(ParcelFileDescriptor socket) {
+ try {
+ final int mark = mNetd.getMarkForProtect();
+ NetworkUtils.markSocket(socket.getFd(), mark);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void setRoutes(String interfaze, List<RouteInfo> routes) {
+ for (RouteInfo route : routes) {
+ try {
+ mNetd.setMarkedForwardingRoute(interfaze, route);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public void setMarkedForwarding(String interfaze) {
+ try {
+ mNetd.setMarkedForwarding(interfaze);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void clearMarkedForwarding(String interfaze) {
+ try {
+ mNetd.clearMarkedForwarding(interfaze);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void addUserForwarding(String interfaze, int uid, boolean forwardDns) {
+ int uidStart = uid * UserHandle.PER_USER_RANGE;
+ int uidEnd = uidStart + UserHandle.PER_USER_RANGE - 1;
+ addUidForwarding(interfaze, uidStart, uidEnd, forwardDns);
+ }
+
+ public void clearUserForwarding(String interfaze, int uid, boolean forwardDns) {
+ int uidStart = uid * UserHandle.PER_USER_RANGE;
+ int uidEnd = uidStart + UserHandle.PER_USER_RANGE - 1;
+ clearUidForwarding(interfaze, uidStart, uidEnd, forwardDns);
+ }
+
+ public void addUidForwarding(String interfaze, int uidStart, int uidEnd,
+ boolean forwardDns) {
+ try {
+ mNetd.setUidRangeRoute(interfaze,uidStart, uidEnd);
+ if (forwardDns) mNetd.setDnsInterfaceForUidRange(interfaze, uidStart, uidEnd);
+ } catch (RemoteException e) {
+ }
+
+ }
+
+ public void clearUidForwarding(String interfaze, int uidStart, int uidEnd,
+ boolean forwardDns) {
+ try {
+ mNetd.clearUidRangeRoute(interfaze, uidStart, uidEnd);
+ if (forwardDns) mNetd.clearDnsInterfaceForUidRange(uidStart, uidEnd);
+ } catch (RemoteException e) {
+ }
+
+ }
}
@Override
@@ -3584,7 +3859,11 @@
final String profileName = new String(mKeyStore.get(Credentials.LOCKDOWN_VPN));
final VpnProfile profile = VpnProfile.decode(
profileName, mKeyStore.get(Credentials.VPN + profileName));
- setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpn, profile));
+ int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized(mVpns) {
+ setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpns.get(user),
+ profile));
+ }
} else {
setLockdownTracker(null);
}
@@ -3676,39 +3955,41 @@
/**
* No connection was possible to the network.
+ * This is NOT a warm sim.
*/
- public static final int CMP_RESULT_CODE_NO_CONNECTION = 0;
+ private static final int CMP_RESULT_CODE_NO_CONNECTION = 0;
/**
* A connection was made to the internet, all is well.
+ * This is NOT a warm sim.
*/
- public static final int CMP_RESULT_CODE_CONNECTABLE = 1;
-
- /**
- * A connection was made but there was a redirection, we appear to be in walled garden.
- * This is an indication of a warm sim on a mobile network.
- */
- public static final int CMP_RESULT_CODE_REDIRECTED = 2;
+ private static final int CMP_RESULT_CODE_CONNECTABLE = 1;
/**
* A connection was made but no dns server was available to resolve a name to address.
- * This is an indication of a warm sim on a mobile network.
+ * This is NOT a warm sim since provisioning network is supported.
*/
- public static final int CMP_RESULT_CODE_NO_DNS = 3;
+ private static final int CMP_RESULT_CODE_NO_DNS = 2;
/**
* A connection was made but could not open a TCP connection.
- * This is an indication of a warm sim on a mobile network.
+ * This is NOT a warm sim since provisioning network is supported.
*/
- public static final int CMP_RESULT_CODE_NO_TCP_CONNECTION = 4;
+ private static final int CMP_RESULT_CODE_NO_TCP_CONNECTION = 3;
+
+ /**
+ * A connection was made but there was a redirection, we appear to be in walled garden.
+ * This is an indication of a warm sim on a mobile network such as T-Mobile.
+ */
+ private static final int CMP_RESULT_CODE_REDIRECTED = 4;
/**
* The mobile network is a provisioning network.
- * This is an indication of a warm sim on a mobile network.
+ * This is an indication of a warm sim on a mobile network such as AT&T.
*/
- public static final int CMP_RESULT_CODE_PROVISIONING_NETWORK = 5;
+ private static final int CMP_RESULT_CODE_PROVISIONING_NETWORK = 5;
- AtomicBoolean mIsCheckingMobileProvisioning = new AtomicBoolean(false);
+ private AtomicBoolean mIsCheckingMobileProvisioning = new AtomicBoolean(false);
@Override
public int checkMobileProvisioning(int suggestedTimeOutMs) {
@@ -3737,42 +4018,8 @@
return timeOutMs;
}
- // Start off with notification off
- setProvNotificationVisible(false, ConnectivityManager.TYPE_NONE, null, null);
-
- // See if we've alreadying determined if we've got a provsioning connection
- // if so we don't need to do anything active
- MobileDataStateTracker mdstDefault = (MobileDataStateTracker)
- mNetTrackers[ConnectivityManager.TYPE_MOBILE];
- boolean isDefaultProvisioning = mdstDefault.isProvisioningNetwork();
-
- MobileDataStateTracker mdstHipri = (MobileDataStateTracker)
- mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI];
- boolean isHipriProvisioning = mdstHipri.isProvisioningNetwork();
-
- if (isDefaultProvisioning || isHipriProvisioning) {
- if (mIsNotificationVisible) {
- if (DBG) {
- log("checkMobileProvisioning: provisioning-ignore notification is visible");
- }
- } else {
- NetworkInfo ni = null;
- if (isDefaultProvisioning) {
- ni = mdstDefault.getNetworkInfo();
- }
- if (isHipriProvisioning) {
- ni = mdstHipri.getNetworkInfo();
- }
- String url = getMobileProvisioningUrl();
- if ((ni != null) && (!TextUtils.isEmpty(url))) {
- setProvNotificationVisible(true, ni.getType(), ni.getExtraInfo(), url);
- } else {
- if (DBG) log("checkMobileProvisioning: provisioning but no url, ignore");
- }
- }
- mIsCheckingMobileProvisioning.set(false);
- return timeOutMs;
- }
+ // Start off with mobile notification off
+ setProvNotificationVisible(false, ConnectivityManager.TYPE_MOBILE_HIPRI, null, null);
CheckMp checkMp = new CheckMp(mContext, this);
CheckMp.CallBack cb = new CheckMp.CallBack() {
@@ -3783,7 +4030,9 @@
mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI].getNetworkInfo();
switch(result) {
case CMP_RESULT_CODE_CONNECTABLE:
- case CMP_RESULT_CODE_NO_CONNECTION: {
+ case CMP_RESULT_CODE_NO_CONNECTION:
+ case CMP_RESULT_CODE_NO_DNS:
+ case CMP_RESULT_CODE_NO_TCP_CONNECTION: {
if (DBG) log("CheckMp.onComplete: ignore, connected or no connection");
break;
}
@@ -3795,19 +4044,20 @@
}
if (TextUtils.isEmpty(url) == false) {
if (DBG) log("CheckMp.onComplete: warm (redirected), url=" + url);
- setProvNotificationVisible(true, ni.getType(), ni.getExtraInfo(),
+ setProvNotificationVisible(true,
+ ConnectivityManager.TYPE_MOBILE_HIPRI, ni.getExtraInfo(),
url);
} else {
if (DBG) log("CheckMp.onComplete: warm (redirected), no url");
}
break;
}
- case CMP_RESULT_CODE_NO_DNS:
- case CMP_RESULT_CODE_NO_TCP_CONNECTION: {
+ case CMP_RESULT_CODE_PROVISIONING_NETWORK: {
String url = getMobileProvisioningUrl();
if (TextUtils.isEmpty(url) == false) {
if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), url=" + url);
- setProvNotificationVisible(true, ni.getType(), ni.getExtraInfo(),
+ setProvNotificationVisible(true,
+ ConnectivityManager.TYPE_MOBILE_HIPRI, ni.getExtraInfo(),
url);
} else {
if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), no url");
@@ -3836,8 +4086,28 @@
static class CheckMp extends
AsyncTask<CheckMp.Params, Void, Integer> {
private static final String CHECKMP_TAG = "CheckMp";
+
+ // adb shell setprop persist.checkmp.testfailures 1 to enable testing failures
+ private static boolean mTestingFailures;
+
+ // Choosing 4 loops as half of them will use HTTPS and the other half HTTP
+ private static final int MAX_LOOPS = 4;
+
+ // Number of milli-seconds to complete all of the retires
public static final int MAX_TIMEOUT_MS = 60000;
+
+ // The socket should retry only 5 seconds, the default is longer
private static final int SOCKET_TIMEOUT_MS = 5000;
+
+ // Sleep time for network errors
+ private static final int NET_ERROR_SLEEP_SEC = 3;
+
+ // Sleep time for network route establishment
+ private static final int NET_ROUTE_ESTABLISHMENT_SLEEP_SEC = 3;
+
+ // Short sleep time for polling :(
+ private static final int POLLING_SLEEP_SEC = 1;
+
private Context mContext;
private ConnectivityService mCs;
private TelephonyManager mTm;
@@ -3863,6 +4133,31 @@
}
}
+ // As explained to me by Brian Carlstrom and Kenny Root, Certificates can be
+ // issued by name or ip address, for Google its by name so when we construct
+ // this HostnameVerifier we'll pass the original Uri and use it to verify
+ // the host. If the host name in the original uril fails we'll test the
+ // hostname parameter just incase things change.
+ static class CheckMpHostnameVerifier implements HostnameVerifier {
+ Uri mOrgUri;
+
+ CheckMpHostnameVerifier(Uri orgUri) {
+ mOrgUri = orgUri;
+ }
+
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
+ String orgUriHost = mOrgUri.getHost();
+ boolean retVal = hv.verify(orgUriHost, session) || hv.verify(hostname, session);
+ if (DBG) {
+ log("isMobileOk: hostnameVerify retVal=" + retVal + " hostname=" + hostname
+ + " orgUriHost=" + orgUriHost);
+ }
+ return retVal;
+ }
+ }
+
/**
* The call back object passed in Params. onComplete will be called
* on the main thread.
@@ -3873,6 +4168,13 @@
}
public CheckMp(Context context, ConnectivityService cs) {
+ if (Build.IS_DEBUGGABLE) {
+ mTestingFailures =
+ SystemProperties.getInt("persist.checkmp.testfailures", 0) == 1;
+ } else {
+ mTestingFailures = false;
+ }
+
mContext = context;
mCs = cs;
@@ -3906,8 +4208,26 @@
mParams = params;
if (mCs.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) {
- log("isMobileOk: not mobile capable");
result = CMP_RESULT_CODE_NO_CONNECTION;
+ log("isMobileOk: X not mobile capable result=" + result);
+ return result;
+ }
+
+ // See if we've already determined we've got a provisioning connection,
+ // if so we don't need to do anything active.
+ MobileDataStateTracker mdstDefault = (MobileDataStateTracker)
+ mCs.mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+ boolean isDefaultProvisioning = mdstDefault.isProvisioningNetwork();
+ log("isMobileOk: isDefaultProvisioning=" + isDefaultProvisioning);
+
+ MobileDataStateTracker mdstHipri = (MobileDataStateTracker)
+ mCs.mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI];
+ boolean isHipriProvisioning = mdstHipri.isProvisioningNetwork();
+ log("isMobileOk: isHipriProvisioning=" + isHipriProvisioning);
+
+ if (isDefaultProvisioning || isHipriProvisioning) {
+ result = CMP_RESULT_CODE_PROVISIONING_NETWORK;
+ log("isMobileOk: X default || hipri is provisioning result=" + result);
return result;
}
@@ -3926,7 +4246,7 @@
mCs.setEnableFailFastMobileData(DctConstants.ENABLED);
break;
}
- sleep(1);
+ sleep(POLLING_SLEEP_SEC);
}
}
@@ -3944,7 +4264,7 @@
}
if (VDBG) log("isMobileOk: hipri not started yet");
result = CMP_RESULT_CODE_NO_CONNECTION;
- sleep(1);
+ sleep(POLLING_SLEEP_SEC);
}
// Continue trying to connect until time has run out
@@ -3960,7 +4280,7 @@
log("isMobileOk: not connected ni=" +
mCs.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI));
}
- sleep(1);
+ sleep(POLLING_SLEEP_SEC);
result = CMP_RESULT_CODE_NO_CONNECTION;
continue;
}
@@ -3969,8 +4289,8 @@
MobileDataStateTracker mdst = (MobileDataStateTracker)
mCs.mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI];
if (mdst.isProvisioningNetwork()) {
- if (DBG) log("isMobileOk: isProvisioningNetwork is true, no TCP conn");
- result = CMP_RESULT_CODE_NO_TCP_CONNECTION;
+ result = CMP_RESULT_CODE_PROVISIONING_NETWORK;
+ if (DBG) log("isMobileOk: X isProvisioningNetwork result=" + result);
return result;
} else {
if (DBG) log("isMobileOk: isProvisioningNetwork is false, continue");
@@ -3978,15 +4298,15 @@
// Get of the addresses associated with the url host. We need to use the
// address otherwise HttpURLConnection object will use the name to get
- // the addresses and is will try every address but that will bypass the
+ // the addresses and will try every address but that will bypass the
// route to host we setup and the connection could succeed as the default
// interface might be connected to the internet via wifi or other interface.
InetAddress[] addresses;
try {
addresses = InetAddress.getAllByName(orgUri.getHost());
} catch (UnknownHostException e) {
- log("isMobileOk: UnknownHostException");
result = CMP_RESULT_CODE_NO_DNS;
+ log("isMobileOk: X UnknownHostException result=" + result);
return result;
}
log("isMobileOk: addresses=" + inetAddressesToString(addresses));
@@ -3994,29 +4314,40 @@
// Get the type of addresses supported by this link
LinkProperties lp = mCs.getLinkProperties(
ConnectivityManager.TYPE_MOBILE_HIPRI);
- boolean linkHasIpv4 = hasIPv4Address(lp);
- boolean linkHasIpv6 = hasIPv6Address(lp);
+ boolean linkHasIpv4 = lp.hasIPv4Address();
+ boolean linkHasIpv6 = lp.hasIPv6Address();
log("isMobileOk: linkHasIpv4=" + linkHasIpv4
+ " linkHasIpv6=" + linkHasIpv6);
- // Loop through at most 3 valid addresses or all of the address or until
- // we run out of time
- int loops = Math.min(3, addresses.length);
- for(int validAddr=0, addrTried=0;
- (validAddr < loops) && (addrTried < addresses.length)
- && (SystemClock.elapsedRealtime() < endTime);
- addrTried ++) {
+ final ArrayList<InetAddress> validAddresses =
+ new ArrayList<InetAddress>(addresses.length);
- // Choose the address at random but make sure its type is supported
- InetAddress hostAddr = addresses[rand.nextInt(addresses.length)];
- if (((hostAddr instanceof Inet4Address) && linkHasIpv4)
- || ((hostAddr instanceof Inet6Address) && linkHasIpv6)) {
- // Valid address, so use it
- validAddr += 1;
- } else {
- // Invalid address so try next address
- continue;
+ for (InetAddress addr : addresses) {
+ if (((addr instanceof Inet4Address) && linkHasIpv4) ||
+ ((addr instanceof Inet6Address) && linkHasIpv6)) {
+ validAddresses.add(addr);
}
+ }
+
+ if (validAddresses.size() == 0) {
+ return CMP_RESULT_CODE_NO_CONNECTION;
+ }
+
+ int addrTried = 0;
+ while (true) {
+ // Loop through at most MAX_LOOPS valid addresses or until
+ // we run out of time
+ if (addrTried++ >= MAX_LOOPS) {
+ log("isMobileOk: too many loops tried - giving up");
+ break;
+ }
+ if (SystemClock.elapsedRealtime() >= endTime) {
+ log("isMobileOk: spend too much time - giving up");
+ break;
+ }
+
+ InetAddress hostAddr = validAddresses.get(rand.nextInt(
+ validAddresses.size()));
// Make a route to host so we check the specific interface.
if (mCs.requestRouteToHostAddress(ConnectivityManager.TYPE_MOBILE_HIPRI,
@@ -4024,51 +4355,90 @@
// Wait a short time to be sure the route is established ??
log("isMobileOk:"
+ " wait to establish route to hostAddr=" + hostAddr);
- sleep(3);
+ sleep(NET_ROUTE_ESTABLISHMENT_SLEEP_SEC);
} else {
log("isMobileOk:"
+ " could not establish route to hostAddr=" + hostAddr);
+ // Wait a short time before the next attempt
+ sleep(NET_ERROR_SLEEP_SEC);
continue;
}
- // Rewrite the url to have numeric address to use the specific route.
- // I also set the "Connection" to "Close" as by default "Keep-Alive"
- // is used which is useless in this case.
- URL newUrl = new URL(orgUri.getScheme() + "://"
- + hostAddr.getHostAddress() + orgUri.getPath());
+ // Rewrite the url to have numeric address to use the specific route
+ // using http for half the attempts and https for the other half.
+ // Doing https first and http second as on a redirected walled garden
+ // such as t-mobile uses we get a SocketTimeoutException: "SSL
+ // handshake timed out" which we declare as
+ // CMP_RESULT_CODE_NO_TCP_CONNECTION. We could change this, but by
+ // having http second we will be using logic used for some time.
+ URL newUrl;
+ String scheme = (addrTried <= (MAX_LOOPS/2)) ? "https" : "http";
+ newUrl = new URL(scheme, hostAddr.getHostAddress(),
+ orgUri.getPath());
log("isMobileOk: newUrl=" + newUrl);
HttpURLConnection urlConn = null;
try {
- // Open the connection set the request header and get the response
- urlConn = (HttpURLConnection) newUrl.openConnection(
+ // Open the connection set the request headers and get the response
+ urlConn = (HttpURLConnection)newUrl.openConnection(
java.net.Proxy.NO_PROXY);
+ if (scheme.equals("https")) {
+ ((HttpsURLConnection)urlConn).setHostnameVerifier(
+ new CheckMpHostnameVerifier(orgUri));
+ }
urlConn.setInstanceFollowRedirects(false);
urlConn.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConn.setReadTimeout(SOCKET_TIMEOUT_MS);
urlConn.setUseCaches(false);
urlConn.setAllowUserInteraction(false);
+ // Set the "Connection" to "Close" as by default "Keep-Alive"
+ // is used which is useless in this case.
urlConn.setRequestProperty("Connection", "close");
int responseCode = urlConn.getResponseCode();
- if (responseCode == 204) {
- result = CMP_RESULT_CODE_CONNECTABLE;
- } else {
- result = CMP_RESULT_CODE_REDIRECTED;
- }
- log("isMobileOk: connected responseCode=" + responseCode);
+
+ // For debug display the headers
+ Map<String, List<String>> headers = urlConn.getHeaderFields();
+ log("isMobileOk: headers=" + headers);
+
+ // Close the connection
urlConn.disconnect();
urlConn = null;
- return result;
+
+ if (mTestingFailures) {
+ // Pretend no connection, this tests using http and https
+ result = CMP_RESULT_CODE_NO_CONNECTION;
+ log("isMobileOk: TESTING_FAILURES, pretend no connction");
+ continue;
+ }
+
+ if (responseCode == 204) {
+ // Return
+ result = CMP_RESULT_CODE_CONNECTABLE;
+ log("isMobileOk: X got expected responseCode=" + responseCode
+ + " result=" + result);
+ return result;
+ } else {
+ // Retry to be sure this was redirected, we've gotten
+ // occasions where a server returned 200 even though
+ // the device didn't have a "warm" sim.
+ log("isMobileOk: not expected responseCode=" + responseCode);
+ // TODO - it would be nice in the single-address case to do
+ // another DNS resolve here, but flushing the cache is a bit
+ // heavy-handed.
+ result = CMP_RESULT_CODE_REDIRECTED;
+ }
} catch (Exception e) {
- log("isMobileOk: HttpURLConnection Exception e=" + e);
+ log("isMobileOk: HttpURLConnection Exception" + e);
+ result = CMP_RESULT_CODE_NO_TCP_CONNECTION;
if (urlConn != null) {
urlConn.disconnect();
urlConn = null;
}
+ sleep(NET_ERROR_SLEEP_SEC);
+ continue;
}
}
- result = CMP_RESULT_CODE_NO_TCP_CONNECTION;
- log("isMobileOk: loops|timed out");
+ log("isMobileOk: X loops|timed out result=" + result);
return result;
} catch (Exception e) {
log("isMobileOk: Exception e=" + e);
@@ -4093,7 +4463,7 @@
log("isMobileOk: connected ni=" +
mCs.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI));
}
- sleep(1);
+ sleep(POLLING_SLEEP_SEC);
continue;
}
}
@@ -4158,21 +4528,7 @@
}
}
- public boolean hasIPv4Address(LinkProperties lp) {
- return lp.hasIPv4Address();
- }
-
- // Not implemented in LinkProperties, do it here.
- public boolean hasIPv6Address(LinkProperties lp) {
- for (LinkAddress address : lp.getLinkAddresses()) {
- if (address.getAddress() instanceof Inet6Address) {
- return true;
- }
- }
- return false;
- }
-
- private void log(String s) {
+ private static void log(String s) {
Slog.d(ConnectivityService.TAG, "[" + CHECKMP_TAG + "] " + s);
}
}
@@ -4192,7 +4548,7 @@
private void handleMobileProvisioningAction(String url) {
// Notication mark notification as not visible
- setProvNotificationVisible(false, ConnectivityManager.TYPE_NONE, null, null);
+ setProvNotificationVisible(false, ConnectivityManager.TYPE_MOBILE_HIPRI, null, null);
// If provisioning network handle as a special case,
// otherwise launch browser with the intent directly.
@@ -4204,8 +4560,9 @@
mdst.enableMobileProvisioning(url);
} else {
if (DBG) log("handleMobileProvisioningAction: on default network");
- Intent newIntent =
- new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ Intent newIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
+ Intent.CATEGORY_APP_BROWSER);
+ newIntent.setData(Uri.parse(url));
newIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
Intent.FLAG_ACTIVITY_NEW_TASK);
try {
@@ -4278,14 +4635,14 @@
notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
try {
- notificationManager.notify(NOTIFICATION_ID, 1, notification);
+ notificationManager.notify(NOTIFICATION_ID, networkType, notification);
} catch (NullPointerException npe) {
loge("setNotificaitionVisible: visible notificationManager npe=" + npe);
npe.printStackTrace();
}
} else {
try {
- notificationManager.cancel(NOTIFICATION_ID, 1);
+ notificationManager.cancel(NOTIFICATION_ID, networkType);
} catch (NullPointerException npe) {
loge("setNotificaitionVisible: cancel notificationManager npe=" + npe);
npe.printStackTrace();
@@ -4426,4 +4783,145 @@
enforceConnectivityInternalPermission();
setProvNotificationVisible(visible, networkType, extraInfo, url);
}
+
+ @Override
+ public void setAirplaneMode(boolean enable) {
+ enforceConnectivityInternalPermission();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ final ContentResolver cr = mContext.getContentResolver();
+ 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);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void onUserStart(int userId) {
+ synchronized(mVpns) {
+ Vpn userVpn = mVpns.get(userId);
+ if (userVpn != null) {
+ loge("Starting user already has a VPN");
+ return;
+ }
+ userVpn = new Vpn(mContext, mVpnCallback, mNetd, this, userId);
+ mVpns.put(userId, userVpn);
+ userVpn.startMonitoring(mContext, mTrackerHandler);
+ }
+ }
+
+ private void onUserStop(int userId) {
+ synchronized(mVpns) {
+ Vpn userVpn = mVpns.get(userId);
+ if (userVpn == null) {
+ loge("Stopping user has no VPN");
+ return;
+ }
+ mVpns.delete(userId);
+ }
+ }
+
+ private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) return;
+
+ if (Intent.ACTION_USER_STARTING.equals(action)) {
+ onUserStart(userId);
+ } else if (Intent.ACTION_USER_STOPPING.equals(action)) {
+ onUserStop(userId);
+ }
+ }
+ };
+
+ @Override
+ public LinkQualityInfo getLinkQualityInfo(int networkType) {
+ enforceAccessPermission();
+ if (isNetworkTypeValid(networkType)) {
+ return mNetTrackers[networkType].getLinkQualityInfo();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public LinkQualityInfo getActiveLinkQualityInfo() {
+ enforceAccessPermission();
+ if (isNetworkTypeValid(mActiveDefaultNetwork)) {
+ 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() {
+
+ log("Sampling interval elapsed, updating statistics ..");
+
+ // initialize list of interfaces ..
+ Map<String, SamplingDataTracker.SamplingSnapshot> mapIfaceToSample =
+ new HashMap<String, SamplingDataTracker.SamplingSnapshot>();
+ for (NetworkStateTracker tracker : mNetTrackers) {
+ if (tracker != null) {
+ String ifaceName = tracker.getNetworkInterfaceName();
+ if (ifaceName != null) {
+ mapIfaceToSample.put(ifaceName, null);
+ }
+ }
+ }
+
+ // Read samples for all interfaces
+ SamplingDataTracker.getSamplingSnapshots(mapIfaceToSample);
+
+ // process samples for all networks
+ for (NetworkStateTracker tracker : mNetTrackers) {
+ if (tracker != null) {
+ String ifaceName = tracker.getNetworkInterfaceName();
+ SamplingDataTracker.SamplingSnapshot ss = mapIfaceToSample.get(ifaceName);
+ if (ss != null) {
+ // end the previous sampling cycle
+ tracker.stopSampling(ss);
+ // start a new sampling cycle ..
+ tracker.startSampling(ss);
+ }
+ }
+ }
+
+ log("Done.");
+
+ int samplingIntervalInSeconds = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS,
+ DEFAULT_SAMPLING_INTERVAL_IN_SECONDS);
+
+ if (DBG) log("Setting timer for " + String.valueOf(samplingIntervalInSeconds) + "seconds");
+
+ setAlarm(samplingIntervalInSeconds * 1000, mSampleIntervalElapsedIntent);
+ }
+
+ void setAlarm(int timeoutInMilliseconds, PendingIntent intent) {
+ long wakeupTime = SystemClock.elapsedRealtime() + timeoutInMilliseconds;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, wakeupTime, intent);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
new file mode 100644
index 0000000..56dd7c4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.net.LocalSocket;
+import android.net.LocalServerSocket;
+import android.os.Binder;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import com.android.server.net.BaseNetworkObserver;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Tests for {@link NetworkManagementService}.
+ */
+@LargeTest
+public class NetworkManagementServiceTest extends AndroidTestCase {
+
+ private static final String SOCKET_NAME = "__test__NetworkManagementServiceTest";
+ private NetworkManagementService mNMService;
+ private LocalServerSocket mServerSocket;
+ private LocalSocket mSocket;
+ private OutputStream mOutputStream;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ // TODO: make this unnecessary. runtest might already make it unnecessary.
+ System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
+
+ // Set up a sheltered test environment.
+ BroadcastInterceptingContext context = new BroadcastInterceptingContext(getContext());
+ mServerSocket = new LocalServerSocket(SOCKET_NAME);
+
+ // Start the service and wait until it connects to our socket.
+ mNMService = NetworkManagementService.create(context, SOCKET_NAME);
+ mSocket = mServerSocket.accept();
+ mOutputStream = mSocket.getOutputStream();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ if (mSocket != null) mSocket.close();
+ if (mServerSocket != null) mServerSocket.close();
+ super.tearDown();
+ }
+
+ /**
+ * Sends a message on the netd socket and gives the events some time to make it back.
+ */
+ private void sendMessage(String message) throws IOException {
+ // Strings are null-terminated, so add "\0" at the end.
+ mOutputStream.write((message + "\0").getBytes());
+ }
+
+ private static <T> T expectSoon(T mock) {
+ return verify(mock, timeout(100));
+ }
+
+ /**
+ * Tests that network observers work properly.
+ */
+ public void testNetworkObservers() throws Exception {
+ BaseNetworkObserver observer = mock(BaseNetworkObserver.class);
+ doReturn(new Binder()).when(observer).asBinder(); // Used by registerObserver.
+ mNMService.registerObserver(observer);
+
+ // Forget everything that happened to the mock so far, so we can explicitly verify
+ // everything that happens and does not happen to it from now on.
+ reset(observer);
+
+ // Now send NetworkManagementService messages and ensure that the observer methods are
+ // called. After every valid message we expect a callback soon after; to ensure that
+ // invalid messages don't cause any callbacks, we call verifyNoMoreInteractions at the end.
+
+ /**
+ * Interface changes.
+ */
+ sendMessage("600 Iface added rmnet12");
+ expectSoon(observer).interfaceAdded("rmnet12");
+
+ sendMessage("600 Iface removed eth1");
+ expectSoon(observer).interfaceRemoved("eth1");
+
+ sendMessage("607 Iface removed eth1");
+ // Invalid code.
+
+ sendMessage("600 Iface borked lo down");
+ // Invalid event.
+
+ sendMessage("600 Iface changed clat4 up again");
+ // Extra tokens.
+
+ sendMessage("600 Iface changed clat4 up");
+ expectSoon(observer).interfaceStatusChanged("clat4", true);
+
+ sendMessage("600 Iface linkstate rmnet0 down");
+ expectSoon(observer).interfaceLinkStateChanged("rmnet0", false);
+
+ sendMessage("600 IFACE linkstate clat4 up");
+ // Invalid group.
+
+ /**
+ * Bandwidth control events.
+ */
+ sendMessage("601 limit alert data rmnet_usb0");
+ expectSoon(observer).limitReached("data", "rmnet_usb0");
+
+ sendMessage("601 invalid alert data rmnet0");
+ // Invalid group.
+
+ sendMessage("601 limit increased data rmnet0");
+ // Invalid event.
+
+
+ /**
+ * Interface class activity.
+ */
+ sendMessage("613 IfaceClass active rmnet0");
+ expectSoon(observer).interfaceClassDataActivityChanged("rmnet0", true);
+
+ sendMessage("613 IfaceClass idle eth0");
+ expectSoon(observer).interfaceClassDataActivityChanged("eth0", false);
+
+ sendMessage("613 IfaceClass reallyactive rmnet0");
+ expectSoon(observer).interfaceClassDataActivityChanged("rmnet0", false);
+
+ sendMessage("613 InterfaceClass reallyactive rmnet0");
+ // Invalid group.
+
+
+ /**
+ * IP address changes.
+ */
+ sendMessage("614 Address updated fe80::1/64 wlan0 128 253");
+ expectSoon(observer).addressUpdated("fe80::1/64", "wlan0", 128, 253);
+
+ // There is no "added".
+ sendMessage("614 Address added fe80::1/64 wlan0 128 253");
+ expectSoon(observer).addressRemoved("fe80::1/64", "wlan0", 128, 253);
+
+ sendMessage("614 Address removed 2001:db8::1/64 wlan0 1 0");
+ expectSoon(observer).addressRemoved("2001:db8::1/64", "wlan0", 1, 0);
+
+ sendMessage("666 Address added 2001:db8::1/64 wlan0 1 0");
+ // Invalid code.
+
+ // Make sure nothing else was called.
+ verifyNoMoreInteractions(observer);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index cdc4d78..a1af8cb 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -40,7 +40,6 @@
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
import static org.easymock.EasyMock.anyLong;
-import static org.easymock.EasyMock.aryEq;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.eq;
@@ -63,6 +62,7 @@
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.os.INetworkManagementService;
+import android.os.WorkSource;
import android.telephony.TelephonyManager;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
@@ -73,13 +73,13 @@
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
+import libcore.io.IoUtils;
+
import org.easymock.Capture;
import org.easymock.EasyMock;
import java.io.File;
-import libcore.io.IoUtils;
-
/**
* Tests for {@link NetworkStatsService}.
*/
@@ -878,8 +878,8 @@
mAlarmManager.remove(isA(PendingIntent.class));
expectLastCall().anyTimes();
- mAlarmManager.setInexactRepeating(
- eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), isA(PendingIntent.class));
+ mAlarmManager.set(eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), anyLong(),
+ isA(PendingIntent.class), isA(WorkSource.class));
expectLastCall().atLeastOnce();
mNetManager.setGlobalAlert(anyLong());
@@ -918,8 +918,7 @@
expect(mNetManager.getNetworkStatsUidDetail(eq(UID_ALL))).andReturn(detail).atLeastOnce();
// also include tethering details, since they are folded into UID
- expect(mConnManager.getTetheredIfacePairs()).andReturn(tetherIfacePairs).atLeastOnce();
- expect(mNetManager.getNetworkStatsTethering(aryEq(tetherIfacePairs)))
+ expect(mNetManager.getNetworkStatsTethering())
.andReturn(tetherStats).atLeastOnce();
}