Merge "Fix security problem on PermissionMonitor#hasPermission" into oc-dev am: 5e0b069876 am: 0ebda2d6ca
Change-Id: I5660cafce05a0e3c6edff03bd645d8df329c5d50
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index f478071..467eb9b 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -582,6 +582,8 @@
/** {@hide} */
public static final int MAX_NETWORK_TYPE = TYPE_VPN;
+ private static final int MIN_NETWORK_TYPE = TYPE_MOBILE;
+
/**
* If you want to set the default network preference,you can directly
* change the networkAttributes array in framework's config.xml.
@@ -599,7 +601,7 @@
/**
* @hide
*/
- public final static int REQUEST_ID_UNSET = 0;
+ public static final int REQUEST_ID_UNSET = 0;
/**
* Static unique request used as a tombstone for NetworkCallbacks that have been unregistered.
@@ -607,7 +609,7 @@
* registered and those that were already unregistered.
* @hide
*/
- private final static NetworkRequest ALREADY_UNREGISTERED =
+ private static final NetworkRequest ALREADY_UNREGISTERED =
new NetworkRequest.Builder().clearCapabilities().build();
/**
@@ -640,7 +642,7 @@
*/
@Deprecated
public static boolean isNetworkTypeValid(int networkType) {
- return networkType >= 0 && networkType <= MAX_NETWORK_TYPE;
+ return MIN_NETWORK_TYPE <= networkType && networkType <= MAX_NETWORK_TYPE;
}
/**
@@ -653,6 +655,8 @@
*/
public static String getNetworkTypeName(int type) {
switch (type) {
+ case TYPE_NONE:
+ return "NONE";
case TYPE_MOBILE:
return "MOBILE";
case TYPE_WIFI:
@@ -831,6 +835,29 @@
}
/**
+ * Checks if a VPN app supports always-on mode.
+ *
+ * In order to support the always-on feature, an app has to
+ * <ul>
+ * <li>target {@link VERSION_CODES#N API 24} or above, and
+ * <li>not opt out through the {@link VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}
+ * meta-data field.
+ * </ul>
+ *
+ * @param userId The identifier of the user for whom the VPN app is installed.
+ * @param vpnPackage The canonical package name of the VPN app.
+ * @return {@code true} if and only if the VPN app exists and supports always-on mode.
+ * @hide
+ */
+ public boolean isAlwaysOnVpnPackageSupportedForUser(int userId, @Nullable String vpnPackage) {
+ try {
+ return mService.isAlwaysOnVpnPackageSupported(userId, vpnPackage);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Configures an always-on VPN connection through a specific application.
* This connection is automatically granted and persisted after a reboot.
*
@@ -1098,6 +1125,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS)
public String getCaptivePortalServerUrl() {
try {
return mService.getCaptivePortalServerUrl();
@@ -1715,14 +1743,8 @@
// ignored
}
- /**
- * Return quota status for the current active network, or {@code null} if no
- * network is active. Quota status can change rapidly, so these values
- * shouldn't be cached.
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ /** {@hide} */
+ @Deprecated
public NetworkQuotaInfo getActiveNetworkQuotaInfo() {
try {
return mService.getActiveNetworkQuotaInfo();
@@ -2056,15 +2078,30 @@
* {@code ro.tether.denied} system property, Settings.TETHER_SUPPORTED or
* due to device configuration.
*
+ * <p>If this app does not have permission to use this API, it will always
+ * return false rather than throw an exception.</p>
+ *
+ * <p>If the device has a hotspot provisioning app, the caller is required to hold the
+ * {@link android.Manifest.permission.TETHER_PRIVILEGED} permission.</p>
+ *
+ * <p>Otherwise, this method requires the caller to hold the ability to modify system
+ * settings as determined by {@link android.provider.Settings.System#canWrite}.</p>
+ *
* @return a boolean - {@code true} indicating Tethering is supported.
*
* {@hide}
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @RequiresPermission(anyOf = {android.Manifest.permission.TETHER_PRIVILEGED,
+ android.Manifest.permission.WRITE_SETTINGS})
public boolean isTetheringSupported() {
+ String pkgName = mContext.getOpPackageName();
try {
- return mService.isTetheringSupported();
+ return mService.isTetheringSupported(pkgName);
+ } catch (SecurityException e) {
+ // This API is not available to this caller, but for backward-compatibility
+ // this will just return false instead of throwing.
+ return false;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2094,6 +2131,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
public void startTethering(int type, boolean showProvisioningUi,
final OnStartTetheringCallback callback) {
startTethering(type, showProvisioningUi, callback, null);
@@ -2467,6 +2505,28 @@
}
/**
+ * Returns if the active data network for the given UID is metered. A network
+ * is classified as metered when the user is sensitive to heavy data usage on
+ * that connection due to monetary costs, data limitations or
+ * battery/performance issues. You should check this before doing large
+ * data transfers, and warn the user or delay the operation until another
+ * network is available.
+ *
+ * @return {@code true} if large transfers should be avoided, otherwise
+ * {@code false}.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+ public boolean isActiveNetworkMeteredForUid(int uid) {
+ try {
+ return mService.isActiveNetworkMeteredForUid(uid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* If the LockdownVpn mechanism is enabled, updates the vpn
* with a reload of its profile.
*
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 27729dc..f11372c 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -66,6 +66,7 @@
NetworkQuotaInfo getActiveNetworkQuotaInfo();
boolean isActiveNetworkMetered();
+ boolean isActiveNetworkMeteredForUid(int uid);
boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress);
@@ -75,7 +76,7 @@
int getLastTetherError(String iface);
- boolean isTetheringSupported();
+ boolean isTetheringSupported(String callerPkg);
void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi,
String callerPkg);
@@ -123,6 +124,7 @@
VpnInfo[] getAllVpnInfo();
boolean updateLockdownVpn();
+ boolean isAlwaysOnVpnPackageSupported(int userId, String packageName);
boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown);
String getAlwaysOnVpnPackage(int userId);
diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java
index 6b4f2d5..6e2654e 100644
--- a/core/java/android/net/IpPrefix.java
+++ b/core/java/android/net/IpPrefix.java
@@ -20,6 +20,8 @@
import android.os.Parcelable;
import android.util.Pair;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
@@ -185,6 +187,20 @@
}
/**
+ * @hide
+ */
+ public boolean isIPv6() {
+ return getAddress() instanceof Inet6Address;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isIPv4() {
+ return getAddress() instanceof Inet4Address;
+ }
+
+ /**
* Returns a string representation of this {@code IpPrefix}.
*
* @return a string such as {@code "192.0.2.0/24"} or {@code "2001:db8:1:2::/64"}.
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 6e74f14..bcfe938 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -16,6 +16,15 @@
package android.net;
+import static android.system.OsConstants.IFA_F_DADFAILED;
+import static android.system.OsConstants.IFA_F_DEPRECATED;
+import static android.system.OsConstants.IFA_F_OPTIMISTIC;
+import static android.system.OsConstants.IFA_F_TENTATIVE;
+import static android.system.OsConstants.RT_SCOPE_HOST;
+import static android.system.OsConstants.RT_SCOPE_LINK;
+import static android.system.OsConstants.RT_SCOPE_SITE;
+import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
+
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pair;
@@ -26,15 +35,6 @@
import java.net.InterfaceAddress;
import java.net.UnknownHostException;
-import static android.system.OsConstants.IFA_F_DADFAILED;
-import static android.system.OsConstants.IFA_F_DEPRECATED;
-import static android.system.OsConstants.IFA_F_OPTIMISTIC;
-import static android.system.OsConstants.IFA_F_TENTATIVE;
-import static android.system.OsConstants.RT_SCOPE_HOST;
-import static android.system.OsConstants.RT_SCOPE_LINK;
-import static android.system.OsConstants.RT_SCOPE_SITE;
-import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
-
/**
* Identifies an IP address on a network link.
*
@@ -76,7 +76,7 @@
* RFC 6724 section 3.2.
* @hide
*/
- static int scopeForUnicastAddress(InetAddress addr) {
+ private static int scopeForUnicastAddress(InetAddress addr) {
if (addr.isAnyLocalAddress()) {
return RT_SCOPE_HOST;
}
@@ -101,7 +101,7 @@
* Per RFC 4193 section 8, fc00::/7 identifies these addresses.
*/
private boolean isIPv6ULA() {
- if (address != null && address instanceof Inet6Address) {
+ if (isIPv6()) {
byte[] bytes = address.getAddress();
return ((bytes[0] & (byte)0xfe) == (byte)0xfc);
}
@@ -109,13 +109,29 @@
}
/**
+ * @return true if the address is IPv6.
+ * @hide
+ */
+ public boolean isIPv6() {
+ return address instanceof Inet6Address;
+ }
+
+ /**
+ * @return true if the address is IPv4 or is a mapped IPv4 address.
+ * @hide
+ */
+ public boolean isIPv4() {
+ return address instanceof Inet4Address;
+ }
+
+ /**
* Utility function for the constructors.
*/
private void init(InetAddress address, int prefixLength, int flags, int scope) {
if (address == null ||
address.isMulticastAddress() ||
prefixLength < 0 ||
- ((address instanceof Inet4Address) && prefixLength > 32) ||
+ (address instanceof Inet4Address && prefixLength > 32) ||
(prefixLength > 128)) {
throw new IllegalArgumentException("Bad LinkAddress params " + address +
"/" + prefixLength);
@@ -184,6 +200,7 @@
*/
public LinkAddress(String address, int flags, int scope) {
// This may throw an IllegalArgumentException; catching it is the caller's responsibility.
+ // TODO: consider rejecting mapped IPv4 addresses such as "::ffff:192.0.2.5/24".
Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address);
init(ipAndMask.first, ipAndMask.second, flags, scope);
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 1bb0fbb..f527f77 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -18,14 +18,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.net.ProxyInfo;
-import android.os.Parcelable;
import android.os.Parcel;
+import android.os.Parcelable;
import android.text.TextUtils;
-import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.Inet6Address;
+import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
@@ -504,11 +503,22 @@
}
/**
+ * Make sure this LinkProperties instance contains routes that cover the local subnet
+ * of its link addresses. Add any route that is missing.
+ * @hide
+ */
+ public void ensureDirectlyConnectedRoutes() {
+ for (LinkAddress addr: mLinkAddresses) {
+ addRoute(new RouteInfo(addr, null, mIfaceName));
+ }
+ }
+
+ /**
* Returns all the routes on this link and all the links stacked above it.
* @hide
*/
public List<RouteInfo> getAllRoutes() {
- List<RouteInfo> routes = new ArrayList();
+ List<RouteInfo> routes = new ArrayList<>();
routes.addAll(mRoutes);
for (LinkProperties stacked: mStackedLinks.values()) {
routes.addAll(stacked.getAllRoutes());
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 0a5b9c1..f038c24 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -21,6 +21,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.BitUtils;
+import com.android.internal.util.Preconditions;
import java.util.Objects;
import java.util.StringJoiner;
@@ -416,7 +417,6 @@
/**
* Indicates this network uses a LoWPAN transport.
- * @hide
*/
public static final int TRANSPORT_LOWPAN = 6;
@@ -425,6 +425,11 @@
/** @hide */
public static final int MAX_TRANSPORT = TRANSPORT_LOWPAN;
+ /** @hide */
+ public static boolean isValidTransport(int transportType) {
+ return (MIN_TRANSPORT <= transportType) && (transportType <= MAX_TRANSPORT);
+ }
+
private static final String[] TRANSPORT_NAMES = {
"CELLULAR",
"WIFI",
@@ -449,9 +454,7 @@
* @hide
*/
public NetworkCapabilities addTransportType(int transportType) {
- if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
- throw new IllegalArgumentException("TransportType out of range");
- }
+ checkValidTransportType(transportType);
mTransportTypes |= 1 << transportType;
setNetworkSpecifier(mNetworkSpecifier); // used for exception checking
return this;
@@ -465,9 +468,7 @@
* @hide
*/
public NetworkCapabilities removeTransportType(int transportType) {
- if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
- throw new IllegalArgumentException("TransportType out of range");
- }
+ checkValidTransportType(transportType);
mTransportTypes &= ~(1 << transportType);
setNetworkSpecifier(mNetworkSpecifier); // used for exception checking
return this;
@@ -491,10 +492,7 @@
* @return {@code true} if set on this instance.
*/
public boolean hasTransport(int transportType) {
- if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
- return false;
- }
- return ((mTransportTypes & (1 << transportType)) != 0);
+ return isValidTransport(transportType) && ((mTransportTypes & (1 << transportType)) != 0);
}
private void combineTransportTypes(NetworkCapabilities nc) {
@@ -772,10 +770,11 @@
StringJoiner joiner = new StringJoiner(", ");
- // TODO: consider only enforcing that capabilities are not removed, allowing addition.
// Ignore NOT_METERED being added or removed as it is effectively dynamic. http://b/63326103
// TODO: properly support NOT_METERED as a mutable and requestable capability.
- final long mask = ~MUTABLE_CAPABILITIES & ~(1 << NET_CAPABILITY_NOT_METERED);
+ // Ignore DUN being added or removed. http://b/65257223.
+ final long mask = ~MUTABLE_CAPABILITIES
+ & ~(1 << NET_CAPABILITY_NOT_METERED) & ~(1 << NET_CAPABILITY_DUN);
long oldImmutableCapabilities = this.mNetworkCapabilities & mask;
long newImmutableCapabilities = that.mNetworkCapabilities & mask;
if (oldImmutableCapabilities != newImmutableCapabilities) {
@@ -954,9 +953,14 @@
* @hide
*/
public static String transportNameOf(int transport) {
- if (transport < 0 || TRANSPORT_NAMES.length <= transport) {
+ if (!isValidTransport(transport)) {
return "UNKNOWN";
}
return TRANSPORT_NAMES[transport];
}
+
+ private static void checkValidTransportType(int transport) {
+ Preconditions.checkArgument(
+ isValidTransport(transport), "Invalid TransportType " + transport);
+ }
}
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 42f5feb..818aa21 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -16,8 +16,8 @@
package android.net;
-import android.os.Parcelable;
import android.os.Parcel;
+import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
@@ -121,13 +121,13 @@
private boolean mIsFailover;
private boolean mIsAvailable;
private boolean mIsRoaming;
- private boolean mIsMetered;
/**
* @hide
*/
public NetworkInfo(int type, int subtype, String typeName, String subtypeName) {
- if (!ConnectivityManager.isNetworkTypeValid(type)) {
+ if (!ConnectivityManager.isNetworkTypeValid(type)
+ && type != ConnectivityManager.TYPE_NONE) {
throw new IllegalArgumentException("Invalid network type: " + type);
}
mNetworkType = type;
@@ -153,7 +153,6 @@
mIsFailover = source.mIsFailover;
mIsAvailable = source.mIsAvailable;
mIsRoaming = source.mIsRoaming;
- mIsMetered = source.mIsMetered;
}
}
}
@@ -326,31 +325,6 @@
}
/**
- * Returns if this network is metered. A network is classified as metered
- * when the user is sensitive to heavy data usage on that connection due to
- * monetary costs, data limitations or battery/performance issues. You
- * should check this before doing large data transfers, and warn the user or
- * delay the operation until another network is available.
- *
- * @return {@code true} if large transfers should be avoided, otherwise
- * {@code false}.
- * @hide
- */
- public boolean isMetered() {
- synchronized (this) {
- return mIsMetered;
- }
- }
-
- /** {@hide} */
- @VisibleForTesting
- public void setMetered(boolean isMetered) {
- synchronized (this) {
- mIsMetered = isMetered;
- }
- }
-
- /**
* Reports the current coarse-grained state of the network.
* @return the coarse-grained state
*/
@@ -433,7 +407,6 @@
append(", failover: ").append(mIsFailover).
append(", available: ").append(mIsAvailable).
append(", roaming: ").append(mIsRoaming).
- append(", metered: ").append(mIsMetered).
append("]");
return builder.toString();
}
@@ -456,7 +429,6 @@
dest.writeInt(mIsFailover ? 1 : 0);
dest.writeInt(mIsAvailable ? 1 : 0);
dest.writeInt(mIsRoaming ? 1 : 0);
- dest.writeInt(mIsMetered ? 1 : 0);
dest.writeString(mReason);
dest.writeString(mExtraInfo);
}
@@ -475,7 +447,6 @@
netInfo.mIsFailover = in.readInt() != 0;
netInfo.mIsAvailable = in.readInt() != 0;
netInfo.mIsRoaming = in.readInt() != 0;
- netInfo.mIsMetered = in.readInt() != 0;
netInfo.mReason = in.readString();
netInfo.mExtraInfo = in.readString();
return netInfo;
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 3e99521..823f1cc 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "NetUtils"
#include "jni.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include "NetdClient.h"
#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index d5f6321..9686dd9 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -24,10 +24,15 @@
import android.system.OsConstants;
import android.test.suitebuilder.annotation.SmallTest;
import android.test.suitebuilder.annotation.Suppress;
+import android.util.ArraySet;
+
import junit.framework.TestCase;
import java.net.InetAddress;
-import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
public class LinkPropertiesTest extends TestCase {
@@ -678,4 +683,76 @@
stacked.addRoute(new RouteInfo((IpPrefix) null, stackedAddress));
assertTrue(v6lp.isReachable(DNS1));
}
+
+ @SmallTest
+ public void testLinkPropertiesEnsureDirectlyConnectedRoutes() {
+ // IPv4 case: no route added initially
+ LinkProperties rmnet0 = new LinkProperties();
+ rmnet0.setInterfaceName("rmnet0");
+ rmnet0.addLinkAddress(new LinkAddress("10.0.0.2/8"));
+ RouteInfo directRoute0 = new RouteInfo(new IpPrefix("10.0.0.0/8"), null,
+ rmnet0.getInterfaceName());
+
+ // Since no routes is added explicitly, getAllRoutes() should return empty.
+ assertTrue(rmnet0.getAllRoutes().isEmpty());
+ rmnet0.ensureDirectlyConnectedRoutes();
+ // ensureDirectlyConnectedRoutes() should have added the missing local route.
+ assertEqualRoutes(Collections.singletonList(directRoute0), rmnet0.getAllRoutes());
+
+ // IPv4 case: both direct and default routes added initially
+ LinkProperties rmnet1 = new LinkProperties();
+ rmnet1.setInterfaceName("rmnet1");
+ rmnet1.addLinkAddress(new LinkAddress("10.0.0.3/8"));
+ RouteInfo defaultRoute1 = new RouteInfo((IpPrefix) null,
+ NetworkUtils.numericToInetAddress("10.0.0.1"), rmnet1.getInterfaceName());
+ RouteInfo directRoute1 = new RouteInfo(new IpPrefix("10.0.0.0/8"), null,
+ rmnet1.getInterfaceName());
+ rmnet1.addRoute(defaultRoute1);
+ rmnet1.addRoute(directRoute1);
+
+ // Check added routes
+ assertEqualRoutes(Arrays.asList(defaultRoute1, directRoute1), rmnet1.getAllRoutes());
+ // ensureDirectlyConnectedRoutes() shouldn't change the routes since direct connected
+ // route is already part of the configuration.
+ rmnet1.ensureDirectlyConnectedRoutes();
+ assertEqualRoutes(Arrays.asList(defaultRoute1, directRoute1), rmnet1.getAllRoutes());
+
+ // IPv6 case: only default routes added initially
+ LinkProperties rmnet2 = new LinkProperties();
+ rmnet2.setInterfaceName("rmnet2");
+ rmnet2.addLinkAddress(new LinkAddress("fe80::cafe/64"));
+ rmnet2.addLinkAddress(new LinkAddress("2001:db8::2/64"));
+ RouteInfo defaultRoute2 = new RouteInfo((IpPrefix) null,
+ NetworkUtils.numericToInetAddress("2001:db8::1"), rmnet2.getInterfaceName());
+ RouteInfo directRoute2 = new RouteInfo(new IpPrefix("2001:db8::/64"), null,
+ rmnet2.getInterfaceName());
+ RouteInfo linkLocalRoute2 = new RouteInfo(new IpPrefix("fe80::/64"), null,
+ rmnet2.getInterfaceName());
+ rmnet2.addRoute(defaultRoute2);
+
+ assertEqualRoutes(Arrays.asList(defaultRoute2), rmnet2.getAllRoutes());
+ rmnet2.ensureDirectlyConnectedRoutes();
+ assertEqualRoutes(Arrays.asList(defaultRoute2, directRoute2, linkLocalRoute2),
+ rmnet2.getAllRoutes());
+
+ // Corner case: no interface name
+ LinkProperties rmnet3 = new LinkProperties();
+ rmnet3.addLinkAddress(new LinkAddress("192.168.0.2/24"));
+ RouteInfo directRoute3 = new RouteInfo(new IpPrefix("192.168.0.0/24"), null,
+ rmnet3.getInterfaceName());
+
+ assertTrue(rmnet3.getAllRoutes().isEmpty());
+ rmnet3.ensureDirectlyConnectedRoutes();
+ assertEqualRoutes(Collections.singletonList(directRoute3), rmnet3.getAllRoutes());
+
+ }
+
+ private void assertEqualRoutes(Collection<RouteInfo> expected, Collection<RouteInfo> actual) {
+ Set<RouteInfo> expectedSet = new ArraySet<>(expected);
+ Set<RouteInfo> actualSet = new ArraySet<>(actual);
+ // Duplicated entries in actual routes are considered failures
+ assertEquals(actual.size(), actualSet.size());
+
+ assertEquals(expectedSet, actualSet);
+ }
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 5c4826f..6e8c0d4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.NETID_UNSET;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.getNetworkTypeName;
@@ -40,7 +41,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.net.ConnectivityManager;
@@ -52,10 +52,10 @@
import android.net.INetworkStatsService;
import android.net.LinkProperties;
import android.net.LinkProperties.CompareResult;
+import android.net.MatchAllNetworkSpecifier;
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
-import android.net.MatchAllNetworkSpecifier;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
@@ -91,6 +91,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -124,13 +125,12 @@
import com.android.internal.util.MessageUtils;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.XmlUtils;
-import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.KeepaliveTracker;
+import com.android.server.connectivity.LingerMonitor;
import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.Nat464Xlat;
-import com.android.server.connectivity.LingerMonitor;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkDiagnostics;
import com.android.server.connectivity.NetworkMonitor;
@@ -139,8 +139,8 @@
import com.android.server.connectivity.PacManager;
import com.android.server.connectivity.PermissionMonitor;
import com.android.server.connectivity.Tethering;
-import com.android.server.connectivity.tethering.TetheringDependencies;
import com.android.server.connectivity.Vpn;
+import com.android.server.connectivity.tethering.TetheringDependencies;
import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.LockdownVpnTracker;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -458,6 +458,11 @@
private static final int MAX_WAKELOCK_LOGS = 20;
private final LocalLog mWakelockLogs = new LocalLog(MAX_WAKELOCK_LOGS);
+ private int mTotalWakelockAcquisitions = 0;
+ private int mTotalWakelockReleases = 0;
+ private long mTotalWakelockDurationMs = 0;
+ private long mMaxWakelockDurationMs = 0;
+ private long mLastWakeLockAcquireTimestamp = 0;
// Array of <Network,ReadOnlyLocalLogs> tracking network validation and results
private static final int MAX_VALIDATION_LOGS = 10;
@@ -778,6 +783,13 @@
mNetworksDefined++; // used only in the log() statement below.
}
+ // Do the same for Ethernet, since it's often not specified in the configs, although many
+ // devices can use it via USB host adapters.
+ if (mNetConfigs[TYPE_ETHERNET] == null && hasService(Context.ETHERNET_SERVICE)) {
+ mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET);
+ mNetworksDefined++;
+ }
+
if (VDBG) log("mNetworksDefined=" + mNetworksDefined);
mProtectedNetworks = new ArrayList<Integer>();
@@ -957,7 +969,7 @@
if (!mLockdownEnabled) {
int user = UserHandle.getUserId(uid);
synchronized (mVpns) {
- Vpn vpn = mVpns.get(user);
+ Vpn vpn = getVpn(user);
if (vpn != null && vpn.appliesToUid(uid)) {
return vpn.getUnderlyingNetworks();
}
@@ -1005,7 +1017,7 @@
return false;
}
synchronized (mVpns) {
- final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
+ final Vpn vpn = getVpn(UserHandle.getUserId(uid));
if (vpn != null && vpn.isBlockingUid(uid)) {
return true;
}
@@ -1036,8 +1048,7 @@
/**
* Apply any relevant filters to {@link NetworkState} for the given UID. For
* example, this may mark the network as {@link DetailedState#BLOCKED} based
- * on {@link #isNetworkWithLinkPropertiesBlocked}, or
- * {@link NetworkInfo#isMetered()} based on network policies.
+ * on {@link #isNetworkWithLinkPropertiesBlocked}.
*/
private void filterNetworkStateForUid(NetworkState state, int uid, boolean ignoreBlocked) {
if (state == null || state.networkInfo == null || state.linkProperties == null) return;
@@ -1048,15 +1059,6 @@
if (mLockdownTracker != null) {
mLockdownTracker.augmentNetworkInfo(state.networkInfo);
}
-
- // TODO: apply metered state closer to NetworkAgentInfo
- final long token = Binder.clearCallingIdentity();
- try {
- state.networkInfo.setMetered(mPolicyManager.isNetworkMetered(state));
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(token);
- }
}
/**
@@ -1092,7 +1094,7 @@
final int user = UserHandle.getUserId(uid);
int vpnNetId = NETID_UNSET;
synchronized (mVpns) {
- final Vpn vpn = mVpns.get(user);
+ final Vpn vpn = getVpn(user);
if (vpn != null && vpn.appliesToUid(uid)) vpnNetId = vpn.getNetId();
}
NetworkAgentInfo nai;
@@ -1222,7 +1224,7 @@
if (!mLockdownEnabled) {
synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
+ Vpn vpn = getVpn(userId);
if (vpn != null) {
Network[] networks = vpn.getUnderlyingNetworks();
if (networks != null) {
@@ -1326,30 +1328,35 @@
}
@Override
+ @Deprecated
public NetworkQuotaInfo getActiveNetworkQuotaInfo() {
- enforceAccessPermission();
- final int uid = Binder.getCallingUid();
- final long token = Binder.clearCallingIdentity();
- try {
- final NetworkState state = getUnfilteredActiveNetworkState(uid);
- if (state.networkInfo != null) {
- try {
- return mPolicyManager.getNetworkQuotaInfo(state);
- } catch (RemoteException e) {
- }
- }
- return null;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ Log.w(TAG, "Shame on UID " + Binder.getCallingUid()
+ + " for calling the hidden API getNetworkQuotaInfo(). Shame!");
+ return new NetworkQuotaInfo();
}
@Override
public boolean isActiveNetworkMetered() {
enforceAccessPermission();
- final NetworkInfo info = getActiveNetworkInfo();
- return (info != null) ? info.isMetered() : false;
+ return isActiveNetworkMeteredCommon(Binder.getCallingUid());
+ }
+
+ @Override
+ public boolean isActiveNetworkMeteredForUid(int uid) {
+ enforceConnectivityInternalPermission();
+
+ return isActiveNetworkMeteredCommon(uid);
+ }
+
+ private boolean isActiveNetworkMeteredCommon(int uid) {
+ final NetworkCapabilities caps = getUnfilteredActiveNetworkState(uid).networkCapabilities;
+ if (caps != null) {
+ return !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ } else {
+ // Always return the most conservative value
+ return true;
+ }
}
private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() {
@@ -1510,6 +1517,12 @@
ConnectivityManager.enforceChangePermission(mContext);
}
+ private void enforceSettingsPermission() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.NETWORK_SETTINGS,
+ "ConnectivityService");
+ }
+
private void enforceTetherAccessPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE,
@@ -1959,6 +1972,14 @@
pw.println();
pw.println("NetTransition WakeLock activity (most recent first):");
pw.increaseIndent();
+ pw.println("total acquisitions: " + mTotalWakelockAcquisitions);
+ pw.println("total releases: " + mTotalWakelockReleases);
+ pw.println("cumulative duration: " + (mTotalWakelockDurationMs / 1000) + "s");
+ pw.println("longest duration: " + (mMaxWakelockDurationMs / 1000) + "s");
+ if (mTotalWakelockAcquisitions > mTotalWakelockReleases) {
+ long duration = SystemClock.elapsedRealtime() - mLastWakeLockAcquireTimestamp;
+ pw.println("currently holding WakeLock for: " + (duration / 1000) + "s");
+ }
mWakelockLogs.reverseDump(fd, pw, args);
pw.decreaseIndent();
}
@@ -2023,16 +2044,7 @@
break;
}
case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
- if (VDBG) {
- log("Update of LinkProperties for " + nai.name() +
- "; created=" + nai.created +
- "; everConnected=" + nai.everConnected);
- }
- LinkProperties oldLp = nai.linkProperties;
- synchronized (nai) {
- nai.linkProperties = (LinkProperties)msg.obj;
- }
- if (nai.everConnected) updateLinkProperties(nai, oldLp);
+ handleUpdateLinkProperties(nai, (LinkProperties) msg.obj);
break;
}
case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
@@ -2213,7 +2225,7 @@
// A network factory has connected. Send it all current NetworkRequests.
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
if (nri.request.isListen()) continue;
- NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
+ NetworkAgentInfo nai = getNetworkForRequest(nri.request.requestId);
ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK,
(nai != null ? nai.getCurrentScore() : 0), 0, nri.request);
}
@@ -2281,7 +2293,7 @@
}
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
mNetworkAgentInfos.remove(msg.replyTo);
- updateClat(null, nai.linkProperties, nai);
+ nai.maybeStopClat();
synchronized (mNetworkForNetId) {
// Remove the NetworkAgent, but don't mark the netId as
// available until we've told netd to delete it below.
@@ -2290,9 +2302,9 @@
// Remove all previously satisfied requests.
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest request = nai.requestAt(i);
- NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId);
+ NetworkAgentInfo currentNetwork = getNetworkForRequest(request.requestId);
if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
- mNetworkForRequestId.remove(request.requestId);
+ clearNetworkForRequest(request.requestId);
sendUpdatedScoreToFactories(request, 0);
}
}
@@ -2368,7 +2380,7 @@
}
}
rematchAllNetworksAndRequests(null, 0);
- if (nri.request.isRequest() && mNetworkForRequestId.get(nri.request.requestId) == null) {
+ if (nri.request.isRequest() && getNetworkForRequest(nri.request.requestId) == null) {
sendUpdatedScoreToFactories(nri.request, 0);
}
}
@@ -2423,7 +2435,7 @@
// 2. Unvalidated WiFi will not be reaped when validated cellular
// is currently satisfying the request. This is desirable when
// WiFi ends up validating and out scoring cellular.
- mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() <
+ getNetworkForRequest(nri.request.requestId).getCurrentScore() <
nai.getCurrentScoreAsValidated())) {
return false;
}
@@ -2450,7 +2462,7 @@
if (mNetworkRequests.get(nri.request) == null) {
return;
}
- if (mNetworkForRequestId.get(nri.request.requestId) != null) {
+ if (getNetworkForRequest(nri.request.requestId) != null) {
return;
}
if (VDBG || (DBG && nri.request.isRequest())) {
@@ -2490,7 +2502,7 @@
mNetworkRequestInfoLogs.log("RELEASE " + nri);
if (nri.request.isRequest()) {
boolean wasKept = false;
- NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
+ NetworkAgentInfo nai = getNetworkForRequest(nri.request.requestId);
if (nai != null) {
boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
nai.removeRequest(nri.request.requestId);
@@ -2507,7 +2519,7 @@
} else {
wasKept = true;
}
- mNetworkForRequestId.remove(nri.request.requestId);
+ clearNetworkForRequest(nri.request.requestId);
if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
// Went from foreground to background.
updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
@@ -2759,7 +2771,8 @@
enforceAccessPermission();
NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
- if (nai != null && !nai.networkInfo.isMetered()) {
+ if (nai != null && nai.networkCapabilities
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) {
return ConnectivityManager.MULTIPATH_PREFERENCE_UNMETERED;
}
@@ -2970,12 +2983,16 @@
return mTethering.getTetheredDhcpRanges();
}
+ @Override
+ public boolean isTetheringSupported(String callerPkg) {
+ ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
+ return isTetheringSupported();
+ }
+
// if ro.tether.denied = true we default to no tethering
// gservices could set the secure setting to 1 though to enable it on a build where it
// had previously been turned off.
- @Override
- public boolean isTetheringSupported() {
- enforceTetherAccessPermission();
+ private boolean isTetheringSupported() {
int defaultVal = encodeBool(!mSystemProperties.get("ro.tether.denied").equals("true"));
boolean tetherSupported = toBool(Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.TETHER_SUPPORTED, defaultVal));
@@ -3021,6 +3038,8 @@
return;
}
mNetTransitionWakeLock.acquire();
+ mLastWakeLockAcquireTimestamp = SystemClock.elapsedRealtime();
+ mTotalWakelockAcquisitions++;
}
mWakelockLogs.log("ACQUIRE for " + forWhom);
Message msg = mHandler.obtainMessage(EVENT_EXPIRE_NET_TRANSITION_WAKELOCK);
@@ -3053,6 +3072,10 @@
return;
}
mNetTransitionWakeLock.release();
+ long lockDuration = SystemClock.elapsedRealtime() - mLastWakeLockAcquireTimestamp;
+ mTotalWakelockDurationMs += lockDuration;
+ mMaxWakelockDurationMs = Math.max(mMaxWakelockDurationMs, lockDuration);
+ mTotalWakelockReleases++;
}
mWakelockLogs.log(String.format("RELEASE (%s)", event));
}
@@ -3401,7 +3424,7 @@
throwIfLockdownEnabled();
synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
+ Vpn vpn = getVpn(userId);
if (vpn != null) {
return vpn.prepare(oldPackage, newPackage);
} else {
@@ -3428,7 +3451,7 @@
enforceCrossUserPermission(userId);
synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
+ Vpn vpn = getVpn(userId);
if (vpn != null) {
vpn.setPackageAuthorization(packageName, authorized);
}
@@ -3447,7 +3470,7 @@
throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
- return mVpns.get(user).establish(config);
+ return getVpn(user).establish(config);
}
}
@@ -3464,7 +3487,7 @@
}
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
- mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress);
+ getVpn(user).startLegacyVpn(profile, mKeyStore, egress);
}
}
@@ -3478,7 +3501,7 @@
enforceCrossUserPermission(userId);
synchronized (mVpns) {
- return mVpns.get(userId).getLegacyVpnInfo();
+ return getVpn(userId).getLegacyVpnInfo();
}
}
@@ -3542,7 +3565,7 @@
public VpnConfig getVpnConfig(int userId) {
enforceCrossUserPermission(userId);
synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
+ Vpn vpn = getVpn(userId);
if (vpn != null) {
return vpn.getVpnConfig();
} else {
@@ -3576,7 +3599,7 @@
}
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
- Vpn vpn = mVpns.get(user);
+ Vpn vpn = getVpn(user);
if (vpn == null) {
Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
return false;
@@ -3623,7 +3646,7 @@
*/
private boolean startAlwaysOnVpn(int userId) {
synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
+ Vpn vpn = getVpn(userId);
if (vpn == null) {
// Shouldn't happen as all codepaths that point here should have checked the Vpn
// exists already.
@@ -3636,6 +3659,21 @@
}
@Override
+ public boolean isAlwaysOnVpnPackageSupported(int userId, String packageName) {
+ enforceSettingsPermission();
+ enforceCrossUserPermission(userId);
+
+ synchronized (mVpns) {
+ Vpn vpn = getVpn(userId);
+ if (vpn == null) {
+ Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ return false;
+ }
+ return vpn.isAlwaysOnPackageSupported(packageName);
+ }
+ }
+
+ @Override
public boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown) {
enforceConnectivityInternalPermission();
enforceCrossUserPermission(userId);
@@ -3646,7 +3684,7 @@
}
synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
+ Vpn vpn = getVpn(userId);
if (vpn == null) {
Slog.w(TAG, "User " + userId + " has no Vpn configuration");
return false;
@@ -3668,7 +3706,7 @@
enforceCrossUserPermission(userId);
synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
+ Vpn vpn = getVpn(userId);
if (vpn == null) {
Slog.w(TAG, "User " + userId + " has no Vpn configuration");
return null;
@@ -3784,6 +3822,9 @@
public void setProvisioningNotificationVisible(boolean visible, int networkType,
String action) {
enforceConnectivityInternalPermission();
+ if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
+ return;
+ }
final long ident = Binder.clearCallingIdentity();
try {
// Concatenate the range of types onto the range of NetIDs.
@@ -3811,22 +3852,38 @@
private void onUserStart(int userId) {
synchronized (mVpns) {
- Vpn userVpn = mVpns.get(userId);
+ Vpn userVpn = getVpn(userId);
if (userVpn != null) {
loge("Starting user already has a VPN");
return;
}
userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId);
- mVpns.put(userId, userVpn);
+ setVpn(userId, userVpn);
}
if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
updateLockdownVpn();
}
}
+ /** @hide */
+ @VisibleForTesting
+ Vpn getVpn(int userId) {
+ synchronized (mVpns) {
+ return mVpns.get(userId);
+ }
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ void setVpn(int userId, Vpn userVpn) {
+ synchronized (mVpns) {
+ mVpns.put(userId, userVpn);
+ }
+ }
+
private void onUserStop(int userId) {
synchronized (mVpns) {
- Vpn userVpn = mVpns.get(userId);
+ Vpn userVpn = getVpn(userId);
if (userVpn == null) {
loge("Stopped user has no VPN");
return;
@@ -4275,7 +4332,8 @@
* and the are the highest scored network available.
* the are keyed off the Requests requestId.
*/
- // TODO: Yikes, this is accessed on multiple threads: add synchronization.
+ // NOTE: Accessed on multiple threads, must be synchronized on itself.
+ @GuardedBy("mNetworkForRequestId")
private final SparseArray<NetworkAgentInfo> mNetworkForRequestId =
new SparseArray<NetworkAgentInfo>();
@@ -4305,8 +4363,26 @@
// priority networks like Wi-Fi are active.
private final NetworkRequest mDefaultMobileDataRequest;
+ private NetworkAgentInfo getNetworkForRequest(int requestId) {
+ synchronized (mNetworkForRequestId) {
+ return mNetworkForRequestId.get(requestId);
+ }
+ }
+
+ private void clearNetworkForRequest(int requestId) {
+ synchronized (mNetworkForRequestId) {
+ mNetworkForRequestId.remove(requestId);
+ }
+ }
+
+ private void setNetworkForRequest(int requestId, NetworkAgentInfo nai) {
+ synchronized (mNetworkForRequestId) {
+ mNetworkForRequestId.put(requestId, nai);
+ }
+ }
+
private NetworkAgentInfo getDefaultNetwork() {
- return mNetworkForRequestId.get(mDefaultRequest.requestId);
+ return getNetworkForRequest(mDefaultRequest.requestId);
}
private boolean isDefaultNetwork(NetworkAgentInfo nai) {
@@ -4322,11 +4398,13 @@
int currentScore, NetworkMisc networkMisc) {
enforceConnectivityInternalPermission();
+ LinkProperties lp = new LinkProperties(linkProperties);
+ lp.ensureDirectlyConnectedRoutes();
// TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
// satisfies mDefaultRequest.
final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
- new Network(reserveNetId()), new NetworkInfo(networkInfo), new LinkProperties(
- linkProperties), new NetworkCapabilities(networkCapabilities), currentScore,
+ new Network(reserveNetId()), new NetworkInfo(networkInfo), lp,
+ new NetworkCapabilities(networkCapabilities), currentScore,
mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this);
synchronized (this) {
nai.networkMonitor.systemReady = mSystemReady;
@@ -4371,7 +4449,8 @@
updateRoutes(newLp, oldLp, netId);
updateDnses(newLp, oldLp, netId);
- updateClat(newLp, oldLp, networkAgent);
+ // Start or stop clat accordingly to network state.
+ networkAgent.updateClat(mNetd);
if (isDefaultNetwork(networkAgent)) {
handleApplyDefaultProxy(newLp.getHttpProxy());
} else {
@@ -4386,18 +4465,6 @@
mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
}
- private void updateClat(LinkProperties newLp, LinkProperties oldLp, NetworkAgentInfo nai) {
- final boolean wasRunningClat = nai.clatd != null && nai.clatd.isStarted();
- final boolean shouldRunClat = Nat464Xlat.requiresClat(nai);
-
- if (!wasRunningClat && shouldRunClat) {
- nai.clatd = new Nat464Xlat(mContext, mNetd, mTrackerHandler, nai);
- nai.clatd.start();
- } else if (wasRunningClat && !shouldRunClat) {
- nai.clatd.stop();
- }
- }
-
private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) {
// Marks are only available on WiFi interaces. Checking for
// marks on unsupported interfaces is harmless.
@@ -4571,11 +4638,15 @@
*/
private void updateCapabilities(
int oldScore, NetworkAgentInfo nai, NetworkCapabilities networkCapabilities) {
- // Sanity check: a NetworkAgent should not change its static capabilities or parameters.
- if (nai.everConnected) {
+ // Once a NetworkAgent is connected, complain if some immutable capabilities are removed.
+ if (nai.everConnected && !nai.networkCapabilities.satisfiedByImmutableNetworkCapabilities(
+ networkCapabilities)) {
+ // TODO: consider not complaining when a network agent degrade its capabilities if this
+ // does not cause any request (that is not a listen) currently matching that agent to
+ // stop being matched by the updated agent.
String diff = nai.networkCapabilities.describeImmutableDifferences(networkCapabilities);
if (!TextUtils.isEmpty(diff)) {
- Slog.wtf(TAG, "BUG: " + nai + " changed immutable capabilities:" + diff);
+ Slog.wtf(TAG, "BUG: " + nai + " lost immutable capabilities:" + diff);
}
}
@@ -4628,6 +4699,27 @@
}
}
+ public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
+ if (mNetworkForNetId.get(nai.network.netId) != nai) {
+ // Ignore updates for disconnected networks
+ return;
+ }
+ // newLp is already a defensive copy.
+ newLp.ensureDirectlyConnectedRoutes();
+ if (VDBG) {
+ log("Update of LinkProperties for " + nai.name() +
+ "; created=" + nai.created +
+ "; everConnected=" + nai.everConnected);
+ }
+ LinkProperties oldLp = nai.linkProperties;
+ synchronized (nai) {
+ nai.linkProperties = newLp;
+ }
+ if (nai.everConnected) {
+ updateLinkProperties(nai, oldLp);
+ }
+ }
+
private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
@@ -4849,7 +4941,7 @@
// requests or not, and doesn't affect the network's score.
if (nri.request.isListen()) continue;
- final NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
+ final NetworkAgentInfo currentNetwork = getNetworkForRequest(nri.request.requestId);
final boolean satisfies = newNetwork.satisfies(nri.request);
if (newNetwork == currentNetwork && satisfies) {
if (VDBG) {
@@ -4881,7 +4973,7 @@
if (VDBG) log(" accepting network in place of null");
}
newNetwork.unlingerRequest(nri.request);
- mNetworkForRequestId.put(nri.request.requestId, newNetwork);
+ setNetworkForRequest(nri.request.requestId, newNetwork);
if (!newNetwork.addRequest(nri.request)) {
Slog.wtf(TAG, "BUG: " + newNetwork.name() + " already has " + nri.request);
}
@@ -4915,7 +5007,7 @@
}
newNetwork.removeRequest(nri.request.requestId);
if (currentNetwork == newNetwork) {
- mNetworkForRequestId.remove(nri.request.requestId);
+ clearNetworkForRequest(nri.request.requestId);
sendUpdatedScoreToFactories(nri.request, 0);
} else {
Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
@@ -4944,12 +5036,12 @@
if (!newNetwork.networkCapabilities.equalRequestableCapabilities(nc)) {
Slog.wtf(TAG, String.format(
"BUG: %s changed requestable capabilities during rematch: %s -> %s",
- nc, newNetwork.networkCapabilities));
+ newNetwork.name(), nc, newNetwork.networkCapabilities));
}
if (newNetwork.getCurrentScore() != score) {
Slog.wtf(TAG, String.format(
"BUG: %s changed score during rematch: %d -> %d",
- score, newNetwork.getCurrentScore()));
+ newNetwork.name(), score, newNetwork.getCurrentScore()));
}
// Second pass: process all listens.
@@ -5363,7 +5455,7 @@
throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
- return mVpns.get(user).addAddress(address, prefixLength);
+ return getVpn(user).addAddress(address, prefixLength);
}
}
@@ -5372,7 +5464,7 @@
throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
- return mVpns.get(user).removeAddress(address, prefixLength);
+ return getVpn(user).removeAddress(address, prefixLength);
}
}
@@ -5382,7 +5474,7 @@
int user = UserHandle.getUserId(Binder.getCallingUid());
boolean success;
synchronized (mVpns) {
- success = mVpns.get(user).setUnderlyingNetworks(networks);
+ success = getVpn(user).setUnderlyingNetworks(networks);
}
if (success) {
notifyIfacesChangedForNetworkStats();
@@ -5392,6 +5484,7 @@
@Override
public String getCaptivePortalServerUrl() {
+ enforceConnectivityInternalPermission();
return NetworkMonitor.getCaptivePortalServerHttpUrl(mContext);
}
@@ -5489,6 +5582,11 @@
return new WakeupMessage(c, h, s, cmd, 0, 0, obj);
}
+ @VisibleForTesting
+ public boolean hasService(String name) {
+ return ServiceManager.checkService(name) != null;
+ }
+
private void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) {
int newNetid = NETID_UNSET;
int prevNetid = NETID_UNSET;
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index b390884..e6585ad 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -16,36 +16,36 @@
package com.android.server.connectivity;
-import java.net.Inet4Address;
-
-import android.content.Context;
import android.net.InterfaceConfiguration;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
-import android.net.NetworkAgent;
import android.net.RouteInfo;
-import android.os.Handler;
-import android.os.Message;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.util.Slog;
-import com.android.server.net.BaseNetworkObserver;
import com.android.internal.util.ArrayUtils;
+import com.android.server.net.BaseNetworkObserver;
+
+import java.net.Inet4Address;
+import java.util.Objects;
/**
- * @hide
+ * Class to manage a 464xlat CLAT daemon. Nat464Xlat is not thread safe and should be manipulated
+ * from a consistent and unique thread context. It is the responsibility of ConnectivityService to
+ * call into this class from its own Handler thread.
*
- * Class to manage a 464xlat CLAT daemon.
+ * @hide
*/
public class Nat464Xlat extends BaseNetworkObserver {
- private static final String TAG = "Nat464Xlat";
+ private static final String TAG = Nat464Xlat.class.getSimpleName();
// This must match the interface prefix in clatd.c.
private static final String CLAT_PREFIX = "v4-";
- // The network types we will start clatd on.
+ // The network types we will start clatd on,
+ // allowing clat only on networks for which we can support IPv6-only.
private static final int[] NETWORK_TYPES = {
ConnectivityManager.TYPE_MOBILE,
ConnectivityManager.TYPE_WIFI,
@@ -54,33 +54,23 @@
private final INetworkManagementService mNMService;
- // ConnectivityService Handler for LinkProperties updates.
- private final Handler mHandler;
-
// The network we're running on, and its type.
private final NetworkAgentInfo mNetwork;
- // Internal state variables.
- //
- // The possible states are:
- // - Idle: start() not called. Everything is null.
- // - Starting: start() called. Interfaces are non-null. isStarted() returns true.
- // mIsRunning is false.
- // - Running: start() called, and interfaceLinkStateChanged() told us that mIface is up.
- // mIsRunning is true.
- //
- // Once mIface is non-null and isStarted() is true, methods called by ConnectivityService on
- // its handler thread must not modify any internal state variables; they are only updated by the
- // interface observers, called on the notification threads.
+ private enum State {
+ IDLE, // start() not called. Base iface and stacked iface names are null.
+ STARTING, // start() called. Base iface and stacked iface names are known.
+ RUNNING, // start() called, and the stacked iface is known to be up.
+ STOPPING; // stop() called, this Nat464Xlat is still registered as a network observer for
+ // the stacked interface.
+ }
+
private String mBaseIface;
private String mIface;
- private boolean mIsRunning;
+ private State mState = State.IDLE;
- public Nat464Xlat(
- Context context, INetworkManagementService nmService,
- Handler handler, NetworkAgentInfo nai) {
+ public Nat464Xlat(INetworkManagementService nmService, NetworkAgentInfo nai) {
mNMService = nmService;
- mHandler = handler;
mNetwork = nai;
}
@@ -90,34 +80,112 @@
* @return true if the network requires clat, false otherwise.
*/
public static boolean requiresClat(NetworkAgentInfo nai) {
+ // TODO: migrate to NetworkCapabilities.TRANSPORT_*.
final int netType = nai.networkInfo.getType();
+ final boolean supported = ArrayUtils.contains(NETWORK_TYPES, nai.networkInfo.getType());
+ // TODO: this should also consider if the network is in SUSPENDED state to avoid stopping
+ // clatd in SUSPENDED state.
final boolean connected = nai.networkInfo.isConnected();
+ // We only run clat on networks that don't have a native IPv4 address.
final boolean hasIPv4Address =
- (nai.linkProperties != null) ? nai.linkProperties.hasIPv4Address() : false;
- // Only support clat on mobile and wifi for now, because these are the only IPv6-only
- // networks we can connect to.
- return connected && !hasIPv4Address && ArrayUtils.contains(NETWORK_TYPES, netType);
+ (nai.linkProperties != null) && nai.linkProperties.hasIPv4Address();
+ return supported && connected && !hasIPv4Address;
}
/**
- * Determines whether clatd is started. Always true, except a) if start has not yet been called,
- * or b) if our interface was removed.
+ * @return true if clatd has been started and has not yet stopped.
+ * A true result corresponds to internal states STARTING and RUNNING.
*/
public boolean isStarted() {
- return mIface != null;
+ return mState != State.IDLE;
}
/**
- * Clears internal state. Must not be called by ConnectivityService.
+ * @return true if clatd has been started but the stacked interface is not yet up.
*/
- private void clear() {
+ public boolean isStarting() {
+ return mState == State.STARTING;
+ }
+
+ /**
+ * @return true if clatd has been started and the stacked interface is up.
+ */
+ public boolean isRunning() {
+ return mState == State.RUNNING;
+ }
+
+ /**
+ * @return true if clatd has been stopped.
+ */
+ public boolean isStopping() {
+ return mState == State.STOPPING;
+ }
+
+ /**
+ * Start clatd, register this Nat464Xlat as a network observer for the stacked interface,
+ * and set internal state.
+ */
+ private void enterStartingState(String baseIface) {
+ try {
+ mNMService.registerObserver(this);
+ } catch(RemoteException e) {
+ Slog.e(TAG,
+ "startClat: Can't register interface observer for clat on " + mNetwork.name());
+ return;
+ }
+ try {
+ mNMService.startClatd(baseIface);
+ } catch(RemoteException|IllegalStateException e) {
+ Slog.e(TAG, "Error starting clatd on " + baseIface, e);
+ }
+ mIface = CLAT_PREFIX + baseIface;
+ mBaseIface = baseIface;
+ mState = State.STARTING;
+ }
+
+ /**
+ * Enter running state just after getting confirmation that the stacked interface is up, and
+ * turn ND offload off if on WiFi.
+ */
+ private void enterRunningState() {
+ maybeSetIpv6NdOffload(mBaseIface, false);
+ mState = State.RUNNING;
+ }
+
+ /**
+ * Stop clatd, and turn ND offload on if it had been turned off.
+ */
+ private void enterStoppingState() {
+ if (isRunning()) {
+ maybeSetIpv6NdOffload(mBaseIface, true);
+ }
+
+ try {
+ mNMService.stopClatd(mBaseIface);
+ } catch(RemoteException|IllegalStateException e) {
+ Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
+ }
+
+ mState = State.STOPPING;
+ }
+
+ /**
+ * Unregister as a base observer for the stacked interface, and clear internal state.
+ */
+ private void enterIdleState() {
+ try {
+ mNMService.unregisterObserver(this);
+ } catch(RemoteException|IllegalStateException e) {
+ Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface, e);
+ }
+
mIface = null;
mBaseIface = null;
- mIsRunning = false;
+ mState = State.IDLE;
}
/**
- * Starts the clat daemon. Called by ConnectivityService on the handler thread.
+ * Starts the clat daemon.
*/
public void start() {
if (isStarted()) {
@@ -130,52 +198,30 @@
return;
}
- try {
- mNMService.registerObserver(this);
- } catch(RemoteException e) {
- Slog.e(TAG, "startClat: Can't register interface observer for clat on " + mNetwork);
- return;
- }
-
- mBaseIface = mNetwork.linkProperties.getInterfaceName();
- if (mBaseIface == null) {
+ String baseIface = mNetwork.linkProperties.getInterfaceName();
+ if (baseIface == null) {
Slog.e(TAG, "startClat: Can't start clat on null interface");
return;
}
- mIface = CLAT_PREFIX + mBaseIface;
- // From now on, isStarted() will return true.
-
- Slog.i(TAG, "Starting clatd on " + mBaseIface);
- try {
- mNMService.startClatd(mBaseIface);
- } catch(RemoteException|IllegalStateException e) {
- Slog.e(TAG, "Error starting clatd: " + e);
- }
+ // TODO: should we only do this if mNMService.startClatd() succeeds?
+ Slog.i(TAG, "Starting clatd on " + baseIface);
+ enterStartingState(baseIface);
}
/**
- * Stops the clat daemon. Called by ConnectivityService on the handler thread.
+ * Stops the clat daemon.
*/
public void stop() {
- if (isStarted()) {
- Slog.i(TAG, "Stopping clatd");
- try {
- mNMService.stopClatd(mBaseIface);
- } catch(RemoteException|IllegalStateException e) {
- Slog.e(TAG, "Error stopping clatd: " + e);
- }
- // When clatd stops and its interface is deleted, interfaceRemoved() will notify
- // ConnectivityService and call clear().
- } else {
- Slog.e(TAG, "clatd: already stopped");
+ if (!isStarted()) {
+ return;
}
- }
+ Slog.i(TAG, "Stopping clatd on " + mBaseIface);
- private void updateConnectivityService(LinkProperties lp) {
- Message msg = mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED, lp);
- msg.replyTo = mNetwork.messenger;
- Slog.i(TAG, "sending message to ConnectivityService: " + msg);
- msg.sendToTarget();
+ boolean wasStarting = isStarting();
+ enterStoppingState();
+ if (wasStarting) {
+ enterIdleState();
+ }
}
/**
@@ -184,16 +230,19 @@
* has no idea that 464xlat is running on top of it.
*/
public void fixupLinkProperties(LinkProperties oldLp) {
- if (mNetwork.clatd != null &&
- mIsRunning &&
- mNetwork.linkProperties != null &&
- !mNetwork.linkProperties.getAllInterfaceNames().contains(mIface)) {
- Slog.d(TAG, "clatd running, updating NAI for " + mIface);
- for (LinkProperties stacked: oldLp.getStackedLinks()) {
- if (mIface.equals(stacked.getInterfaceName())) {
- mNetwork.linkProperties.addStackedLink(stacked);
- break;
- }
+ if (!isRunning()) {
+ return;
+ }
+ LinkProperties lp = mNetwork.linkProperties;
+ if (lp == null || lp.getAllInterfaceNames().contains(mIface)) {
+ return;
+ }
+
+ Slog.d(TAG, "clatd running, updating NAI for " + mIface);
+ for (LinkProperties stacked: oldLp.getStackedLinks()) {
+ if (Objects.equals(mIface, stacked.getInterfaceName())) {
+ lp.addStackedLink(stacked);
+ return;
}
}
}
@@ -227,6 +276,7 @@
}
private void maybeSetIpv6NdOffload(String iface, boolean on) {
+ // TODO: migrate to NetworkCapabilities.TRANSPORT_*.
if (mNetwork.networkInfo.getType() != ConnectivityManager.TYPE_WIFI) {
return;
}
@@ -238,52 +288,63 @@
}
}
+ /**
+ * Adds stacked link on base link and transitions to RUNNING state.
+ */
+ private void handleInterfaceLinkStateChanged(String iface, boolean up) {
+ if (!isStarting() || !up || !Objects.equals(mIface, iface)) {
+ return;
+ }
+
+ LinkAddress clatAddress = getLinkAddress(iface);
+ if (clatAddress == null) {
+ Slog.e(TAG, "clatAddress was null for stacked iface " + iface);
+ return;
+ }
+
+ Slog.i(TAG, String.format("interface %s is up, adding stacked link %s on top of %s",
+ mIface, mIface, mBaseIface));
+ enterRunningState();
+ LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
+ lp.addStackedLink(makeLinkProperties(clatAddress));
+ mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp);
+ }
+
+ /**
+ * Removes stacked link on base link and transitions to IDLE state.
+ */
+ private void handleInterfaceRemoved(String iface) {
+ if (!Objects.equals(mIface, iface)) {
+ return;
+ }
+ if (!isRunning() && !isStopping()) {
+ return;
+ }
+
+ Slog.i(TAG, "interface " + iface + " removed");
+ if (!isStopping()) {
+ // Ensure clatd is stopped if stop() has not been called: this likely means that clatd
+ // has crashed.
+ enterStoppingState();
+ }
+ enterIdleState();
+ LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
+ lp.removeStackedLink(iface);
+ mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp);
+ }
+
@Override
public void interfaceLinkStateChanged(String iface, boolean up) {
- // Called by the InterfaceObserver on its own thread, so can race with stop().
- if (isStarted() && up && mIface.equals(iface)) {
- Slog.i(TAG, "interface " + iface + " is up, mIsRunning " + mIsRunning + "->true");
-
- if (!mIsRunning) {
- LinkAddress clatAddress = getLinkAddress(iface);
- if (clatAddress == null) {
- return;
- }
- mIsRunning = true;
- maybeSetIpv6NdOffload(mBaseIface, false);
- LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
- lp.addStackedLink(makeLinkProperties(clatAddress));
- Slog.i(TAG, "Adding stacked link " + mIface + " on top of " + mBaseIface);
- updateConnectivityService(lp);
- }
- }
+ mNetwork.handler().post(() -> { handleInterfaceLinkStateChanged(iface, up); });
}
@Override
public void interfaceRemoved(String iface) {
- if (isStarted() && mIface.equals(iface)) {
- Slog.i(TAG, "interface " + iface + " removed, mIsRunning " + mIsRunning + "->false");
+ mNetwork.handler().post(() -> { handleInterfaceRemoved(iface); });
+ }
- if (mIsRunning) {
- // The interface going away likely means clatd has crashed. Ask netd to stop it,
- // because otherwise when we try to start it again on the same base interface netd
- // will complain that it's already started.
- //
- // Note that this method can be called by the interface observer at the same time
- // that ConnectivityService calls stop(). In this case, the second call to
- // stopClatd() will just throw IllegalStateException, which we'll ignore.
- try {
- mNMService.unregisterObserver(this);
- mNMService.stopClatd(mBaseIface);
- } catch (RemoteException|IllegalStateException e) {
- // Well, we tried.
- }
- maybeSetIpv6NdOffload(mBaseIface, true);
- LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
- lp.removeStackedLink(mIface);
- clear();
- updateConnectivityService(lp);
- }
- }
+ @Override
+ public String toString() {
+ return "mBaseIface: " + mBaseIface + ", mIface: " + mIface + ", mState: " + mState;
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 2a618bc..e96f4b0 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -27,7 +27,9 @@
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.os.Handler;
+import android.os.INetworkManagementService;
import android.os.Messenger;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
@@ -268,6 +270,14 @@
networkMisc = misc;
}
+ public ConnectivityService connService() {
+ return mConnService;
+ }
+
+ public Handler handler() {
+ return mHandler;
+ }
+
// Functions for manipulating the requests satisfied by this network.
//
// These functions must only called on ConnectivityService's main thread.
@@ -551,6 +561,32 @@
for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
}
+ public void updateClat(INetworkManagementService netd) {
+ if (Nat464Xlat.requiresClat(this)) {
+ maybeStartClat(netd);
+ } else {
+ maybeStopClat();
+ }
+ }
+
+ /** Ensure clat has started for this network. */
+ public void maybeStartClat(INetworkManagementService netd) {
+ if (clatd != null && clatd.isStarted()) {
+ return;
+ }
+ clatd = new Nat464Xlat(netd, this);
+ clatd.start();
+ }
+
+ /** Ensure clat has stopped for this network. */
+ public void maybeStopClat() {
+ if (clatd == null) {
+ return;
+ }
+ clatd.stop();
+ clatd = null;
+ }
+
public String toString() {
return "NetworkAgentInfo{ ni{" + networkInfo + "} " +
"network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " +
@@ -562,13 +598,13 @@
"acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " +
"everCaptivePortalDetected{" + everCaptivePortalDetected + "} " +
"lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} " +
+ "clat{" + clatd + "} " +
"}";
}
public String name() {
return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" +
- networkInfo.getSubtypeName() + ") - " +
- (network == null ? "null" : network.toString()) + "]";
+ networkInfo.getSubtypeName() + ") - " + Objects.toString(network) + "]";
}
// Enables sorting in descending order of score.
diff --git a/core/tests/coretests/src/android/net/IpPrefixTest.java b/tests/net/java/android/net/IpPrefixTest.java
similarity index 93%
rename from core/tests/coretests/src/android/net/IpPrefixTest.java
rename to tests/net/java/android/net/IpPrefixTest.java
index 4f2387d..b5b2c07 100644
--- a/core/tests/coretests/src/android/net/IpPrefixTest.java
+++ b/tests/net/java/android/net/IpPrefixTest.java
@@ -16,18 +16,27 @@
package android.net;
-import android.net.IpPrefix;
-import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
-import java.net.InetAddress;
-import java.util.Random;
-import junit.framework.TestCase;
-
-import static android.test.MoreAsserts.assertNotEqual;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
-public class IpPrefixTest extends TestCase {
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import java.net.InetAddress;
+import java.util.Random;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IpPrefixTest {
private static InetAddress Address(String addr) {
return InetAddress.parseNumericAddress(addr);
@@ -42,7 +51,7 @@
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xa0
};
- @SmallTest
+ @Test
public void testConstructor() {
IpPrefix p;
try {
@@ -103,6 +112,7 @@
} catch(IllegalArgumentException expected) {}
}
+ @Test
public void testTruncation() {
IpPrefix p;
@@ -170,7 +180,7 @@
assertFalse(o2.equals(o1));
}
- @SmallTest
+ @Test
public void testEquals() {
IpPrefix p1, p2;
@@ -212,7 +222,7 @@
assertAreNotEqual(p1, p2);
}
- @SmallTest
+ @Test
public void testContains() {
IpPrefix p = new IpPrefix("2001:db8:f00::ace:d00d/127");
assertTrue(p.contains(Address("2001:db8:f00::ace:d00c")));
@@ -240,7 +250,7 @@
assertFalse(ipv4Default.contains(Address("2001:db8::f00")));
}
- @SmallTest
+ @Test
public void testHashCode() {
IpPrefix p = new IpPrefix(new byte[4], 0);
Random random = new Random();
@@ -261,12 +271,12 @@
assertEquals(p.hashCode(), oldP.hashCode());
}
if (p.hashCode() != oldP.hashCode()) {
- assertNotEqual(p, oldP);
+ assertNotEquals(p, oldP);
}
}
}
- @SmallTest
+ @Test
public void testHashCodeIsNotConstant() {
IpPrefix[] prefixes = {
new IpPrefix("2001:db8:f00::ace:d00d/127"),
@@ -276,12 +286,12 @@
};
for (int i = 0; i < prefixes.length; i++) {
for (int j = i + 1; j < prefixes.length; j++) {
- assertNotEqual(prefixes[i].hashCode(), prefixes[j].hashCode());
+ assertNotEquals(prefixes[i].hashCode(), prefixes[j].hashCode());
}
}
}
- @SmallTest
+ @Test
public void testMappedAddressesAreBroken() {
// 192.0.2.0/24 != ::ffff:c000:0204/120, but because we use InetAddress,
// we are unable to comprehend that.
@@ -318,13 +328,16 @@
assertEquals(p, p2);
}
+ @Test
public void testParceling() {
IpPrefix p;
p = new IpPrefix("2001:4860:db8::/64");
assertParcelingIsLossless(p);
+ assertTrue(p.isIPv6());
p = new IpPrefix("192.0.2.0/25");
assertParcelingIsLossless(p);
+ assertTrue(p.isIPv4());
}
}
diff --git a/core/tests/coretests/src/android/net/LinkAddressTest.java b/tests/net/java/android/net/LinkAddressTest.java
similarity index 91%
rename from core/tests/coretests/src/android/net/LinkAddressTest.java
rename to tests/net/java/android/net/LinkAddressTest.java
index adf8d95..c1ad946 100644
--- a/core/tests/coretests/src/android/net/LinkAddressTest.java
+++ b/tests/net/java/android/net/LinkAddressTest.java
@@ -16,6 +16,23 @@
package android.net;
+import static android.system.OsConstants.IFA_F_DADFAILED;
+import static android.system.OsConstants.IFA_F_DEPRECATED;
+import static android.system.OsConstants.IFA_F_OPTIMISTIC;
+import static android.system.OsConstants.IFA_F_PERMANENT;
+import static android.system.OsConstants.IFA_F_TEMPORARY;
+import static android.system.OsConstants.IFA_F_TENTATIVE;
+import static android.system.OsConstants.RT_SCOPE_HOST;
+import static android.system.OsConstants.RT_SCOPE_LINK;
+import static android.system.OsConstants.RT_SCOPE_SITE;
+import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -27,44 +44,35 @@
import java.util.Comparator;
import java.util.List;
-import android.net.LinkAddress;
import android.os.Parcel;
-import android.test.AndroidTestCase;
-import static android.test.MoreAsserts.assertNotEqual;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
-import static android.system.OsConstants.IFA_F_DADFAILED;
-import static android.system.OsConstants.IFA_F_DEPRECATED;
-import static android.system.OsConstants.IFA_F_OPTIMISTIC;
-import static android.system.OsConstants.IFA_F_PERMANENT;
-import static android.system.OsConstants.IFA_F_TEMPORARY;
-import static android.system.OsConstants.IFA_F_TENTATIVE;
-import static android.system.OsConstants.RT_SCOPE_HOST;
-import static android.system.OsConstants.RT_SCOPE_LINK;
-import static android.system.OsConstants.RT_SCOPE_SITE;
-import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
+import org.junit.runner.RunWith;
+import org.junit.Test;
-/**
- * Tests for {@link LinkAddress}.
- */
-public class LinkAddressTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LinkAddressTest {
private static final String V4 = "192.0.2.1";
private static final String V6 = "2001:db8::1";
private static final InetAddress V4_ADDRESS = NetworkUtils.numericToInetAddress(V4);
private static final InetAddress V6_ADDRESS = NetworkUtils.numericToInetAddress(V6);
+ @Test
public void testConstants() {
// RT_SCOPE_UNIVERSE = 0, but all the other constants should be nonzero.
- assertNotEqual(0, RT_SCOPE_HOST);
- assertNotEqual(0, RT_SCOPE_LINK);
- assertNotEqual(0, RT_SCOPE_SITE);
+ assertNotEquals(0, RT_SCOPE_HOST);
+ assertNotEquals(0, RT_SCOPE_LINK);
+ assertNotEquals(0, RT_SCOPE_SITE);
- assertNotEqual(0, IFA_F_DEPRECATED);
- assertNotEqual(0, IFA_F_PERMANENT);
- assertNotEqual(0, IFA_F_TENTATIVE);
+ assertNotEquals(0, IFA_F_DEPRECATED);
+ assertNotEquals(0, IFA_F_PERMANENT);
+ assertNotEquals(0, IFA_F_TENTATIVE);
}
+ @Test
public void testConstructors() throws SocketException {
LinkAddress address;
@@ -74,12 +82,14 @@
assertEquals(25, address.getPrefixLength());
assertEquals(0, address.getFlags());
assertEquals(RT_SCOPE_UNIVERSE, address.getScope());
+ assertTrue(address.isIPv4());
address = new LinkAddress(V6_ADDRESS, 127);
assertEquals(V6_ADDRESS, address.getAddress());
assertEquals(127, address.getPrefixLength());
assertEquals(0, address.getFlags());
assertEquals(RT_SCOPE_UNIVERSE, address.getScope());
+ assertTrue(address.isIPv6());
// Nonsensical flags/scopes or combinations thereof are acceptable.
address = new LinkAddress(V6 + "/64", IFA_F_DEPRECATED | IFA_F_PERMANENT, RT_SCOPE_LINK);
@@ -87,12 +97,14 @@
assertEquals(64, address.getPrefixLength());
assertEquals(IFA_F_DEPRECATED | IFA_F_PERMANENT, address.getFlags());
assertEquals(RT_SCOPE_LINK, address.getScope());
+ assertTrue(address.isIPv6());
address = new LinkAddress(V4 + "/23", 123, 456);
assertEquals(V4_ADDRESS, address.getAddress());
assertEquals(23, address.getPrefixLength());
assertEquals(123, address.getFlags());
assertEquals(456, address.getScope());
+ assertTrue(address.isIPv4());
// InterfaceAddress doesn't have a constructor. Fetch some from an interface.
List<InterfaceAddress> addrs = NetworkInterface.getByName("lo").getInterfaceAddresses();
@@ -174,6 +186,7 @@
} catch(IllegalArgumentException expected) {}
}
+ @Test
public void testAddressScopes() {
assertEquals(RT_SCOPE_HOST, new LinkAddress("::/128").getScope());
assertEquals(RT_SCOPE_HOST, new LinkAddress("0.0.0.0/32").getScope());
@@ -216,6 +229,7 @@
assertFalse(l2 + " unexpectedly equal to " + l1, l2.equals(l1));
}
+ @Test
public void testEqualsAndSameAddressAs() {
LinkAddress l1, l2, l3;
@@ -293,26 +307,17 @@
assertIsSameAddressAs(l1, l2);
}
+ @Test
public void testHashCode() {
- LinkAddress l;
+ LinkAddress l1, l2;
- l = new LinkAddress(V4_ADDRESS, 23);
- assertEquals(-982787, l.hashCode());
+ l1 = new LinkAddress(V4_ADDRESS, 23);
+ l2 = new LinkAddress(V4_ADDRESS, 23, 0, RT_SCOPE_HOST);
+ assertNotEquals(l1.hashCode(), l2.hashCode());
- l = new LinkAddress(V4_ADDRESS, 23, 0, RT_SCOPE_HOST);
- assertEquals(-971865, l.hashCode());
-
- l = new LinkAddress(V4_ADDRESS, 27);
- assertEquals(-982743, l.hashCode());
-
- l = new LinkAddress(V6_ADDRESS, 64);
- assertEquals(1076522926, l.hashCode());
-
- l = new LinkAddress(V6_ADDRESS, 128);
- assertEquals(1076523630, l.hashCode());
-
- l = new LinkAddress(V6_ADDRESS, 128, IFA_F_TENTATIVE, RT_SCOPE_UNIVERSE);
- assertEquals(1076524846, l.hashCode());
+ l1 = new LinkAddress(V6_ADDRESS, 128);
+ l2 = new LinkAddress(V6_ADDRESS, 128, IFA_F_TENTATIVE, RT_SCOPE_UNIVERSE);
+ assertNotEquals(l1.hashCode(), l2.hashCode());
}
private LinkAddress passThroughParcel(LinkAddress l) {
@@ -334,6 +339,7 @@
assertEquals(l, l2);
}
+ @Test
public void testParceling() {
LinkAddress l;
@@ -352,6 +358,7 @@
assertFalse(msg, l.isGlobalPreferred());
}
+ @Test
public void testIsGlobalPreferred() {
LinkAddress l;
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index 7346f9f..743344f 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -17,6 +17,7 @@
package android.net;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
@@ -143,6 +144,14 @@
assertEquals("", nc1.describeImmutableDifferences(nc2));
assertEquals("", nc1.describeImmutableDifferences(nc1));
+ // DUN changing (http://b/65257223)
+ nc1 = new NetworkCapabilities()
+ .addCapability(NET_CAPABILITY_DUN)
+ .addCapability(NET_CAPABILITY_INTERNET);
+ nc2 = new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET);
+ assertEquals("", nc1.describeImmutableDifferences(nc2));
+ assertEquals("", nc1.describeImmutableDifferences(nc1));
+
// Immutable capability changing
nc1 = new NetworkCapabilities()
.addCapability(NET_CAPABILITY_INTERNET)
diff --git a/tests/net/java/android/net/NetworkStatsHistoryTest.java b/tests/net/java/android/net/NetworkStatsHistoryTest.java
index e7b91b5..1c0c14e 100644
--- a/tests/net/java/android/net/NetworkStatsHistoryTest.java
+++ b/tests/net/java/android/net/NetworkStatsHistoryTest.java
@@ -485,6 +485,21 @@
assertTrue(stats.intersects(Long.MIN_VALUE, TEST_START + 1));
}
+ public void testSetValues() throws Exception {
+ stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ stats.recordData(TEST_START, TEST_START + 1,
+ new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L));
+
+ assertEquals(1024L + 2048L, stats.getTotalBytes());
+
+ final NetworkStatsHistory.Entry entry = stats.getValues(0, null);
+ entry.rxBytes /= 2;
+ entry.txBytes *= 2;
+ stats.setValues(0, entry);
+
+ assertEquals(512L + 4096L, stats.getTotalBytes());
+ }
+
private static void assertIndexBeforeAfter(
NetworkStatsHistory stats, int before, int after, long time) {
assertEquals("unexpected before", before, stats.getIndexBefore(time));
diff --git a/tests/net/java/android/net/nsd/NsdManagerTest.java b/tests/net/java/android/net/nsd/NsdManagerTest.java
index adf6998..f77608f 100644
--- a/tests/net/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/net/java/android/net/nsd/NsdManagerTest.java
@@ -349,7 +349,6 @@
chan.connect(mContext, this, msg.replyTo);
chan.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
}
-
}
public static MockServiceHandler create(Context context) {
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 198ddc6..504a2db 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -19,10 +19,16 @@
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
+import static android.net.ConnectivityManager.TYPE_NONE;
+import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.NetworkCapabilities.*;
+import static com.android.internal.util.TestUtils.waitForIdleHandler;
+
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
@@ -61,6 +67,7 @@
import android.net.NetworkMisc;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
+import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.StringNetworkSpecifier;
import android.net.metrics.IpConnectivityLog;
@@ -85,6 +92,7 @@
import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import android.util.LogPrinter;
@@ -95,6 +103,7 @@
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
import com.android.server.connectivity.NetworkMonitor.CaptivePortalProbeResult;
+import com.android.server.connectivity.Vpn;
import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -106,12 +115,16 @@
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BooleanSupplier;
+import java.util.function.Predicate;
/**
* Tests for {@link ConnectivityService}.
@@ -212,20 +225,8 @@
}
}
- /**
- * Block until the given handler becomes idle, or until timeoutMs has passed.
- */
- private static void waitForIdleHandler(HandlerThread handlerThread, int timeoutMs) {
- final ConditionVariable cv = new ConditionVariable();
- final Handler handler = new Handler(handlerThread.getLooper());
- handler.post(() -> cv.open());
- if (!cv.block(timeoutMs)) {
- fail("HandlerThread " + handlerThread.getName() +
- " did not become idle after " + timeoutMs + " ms");
- }
- }
-
- public void waitForIdle(int timeoutMs) {
+ public void waitForIdle(int timeoutMsAsInt) {
+ long timeoutMs = timeoutMsAsInt;
waitForIdleHandler(mService.mHandlerThread, timeoutMs);
waitForIdle(mCellNetworkAgent, timeoutMs);
waitForIdle(mWiFiNetworkAgent, timeoutMs);
@@ -233,7 +234,7 @@
waitForIdleHandler(mService.mHandlerThread, timeoutMs);
}
- public void waitForIdle(MockNetworkAgent agent, int timeoutMs) {
+ public void waitForIdle(MockNetworkAgent agent, long timeoutMs) {
if (agent == null) {
return;
}
@@ -312,6 +313,10 @@
private String mRedirectUrl;
MockNetworkAgent(int transport) {
+ this(transport, new LinkProperties());
+ }
+
+ MockNetworkAgent(int transport, LinkProperties linkProperties) {
final int type = transportToLegacyType(transport);
final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
@@ -327,6 +332,12 @@
case TRANSPORT_CELLULAR:
mScore = 50;
break;
+ case TRANSPORT_WIFI_AWARE:
+ mScore = 20;
+ break;
+ case TRANSPORT_VPN:
+ mScore = 0;
+ break;
default:
throw new UnsupportedOperationException("unimplemented network type");
}
@@ -334,7 +345,7 @@
mHandlerThread.start();
mNetworkAgent = new NetworkAgent(mHandlerThread.getLooper(), mServiceContext,
"Mock-" + typeName, mNetworkInfo, mNetworkCapabilities,
- new LinkProperties(), mScore, new NetworkMisc()) {
+ linkProperties, mScore, new NetworkMisc()) {
@Override
public void unwanted() { mDisconnected.open(); }
@@ -408,6 +419,15 @@
* @param validated Indicate if network should pretend to be validated.
*/
public void connect(boolean validated) {
+ connect(validated, true);
+ }
+
+ /**
+ * Transition this NetworkAgent to CONNECTED state.
+ * @param validated Indicate if network should pretend to be validated.
+ * @param hasInternet Indicate if network should pretend to have NET_CAPABILITY_INTERNET.
+ */
+ public void connect(boolean validated, boolean hasInternet) {
assertEquals("MockNetworkAgents can only be connected once",
mNetworkInfo.getDetailedState(), DetailedState.IDLE);
assertFalse(mNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
@@ -430,7 +450,9 @@
};
mCm.registerNetworkCallback(request, callback);
}
- addCapability(NET_CAPABILITY_INTERNET);
+ if (hasInternet) {
+ addCapability(NET_CAPABILITY_INTERNET);
+ }
connectWithoutInternet();
@@ -766,6 +788,13 @@
return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
}
+ @Override
+ public boolean hasService(String name) {
+ // Currenty, the only relevant service that ConnectivityService checks for is
+ // ETHERNET_SERVICE.
+ return Context.ETHERNET_SERVICE.equals(name);
+ }
+
public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
return mLastCreatedNetworkMonitor;
}
@@ -784,7 +813,10 @@
* Fails if TIMEOUT_MS goes by before {@code conditionVariable} opens.
*/
static private void waitFor(ConditionVariable conditionVariable) {
- assertTrue(conditionVariable.block(TIMEOUT_MS));
+ if (conditionVariable.block(TIMEOUT_MS)) {
+ return;
+ }
+ fail("ConditionVariable was blocked for more than " + TIMEOUT_MS + "ms");
}
@Override
@@ -841,8 +873,10 @@
return TYPE_WIFI;
case TRANSPORT_CELLULAR:
return TYPE_MOBILE;
+ case TRANSPORT_VPN:
+ return TYPE_VPN;
default:
- throw new IllegalStateException("Unknown transport " + transport);
+ return TYPE_NONE;
}
}
@@ -853,6 +887,9 @@
// Test getActiveNetwork()
assertNotNull(mCm.getActiveNetwork());
assertEquals(mCm.getActiveNetwork(), mCm.getActiveNetworkForUid(Process.myUid()));
+ if (!NetworkCapabilities.isValidTransport(transport)) {
+ throw new IllegalStateException("Unknown transport " + transport);
+ }
switch (transport) {
case TRANSPORT_WIFI:
assertEquals(mCm.getActiveNetwork(), mWiFiNetworkAgent.getNetwork());
@@ -861,7 +898,7 @@
assertEquals(mCm.getActiveNetwork(), mCellNetworkAgent.getNetwork());
break;
default:
- throw new IllegalStateException("Unknown transport" + transport);
+ break;
}
// Test getNetworkInfo(Network)
assertNotNull(mCm.getNetworkInfo(mCm.getActiveNetwork()));
@@ -873,13 +910,14 @@
}
private void verifyNoNetwork() {
+ waitForIdle();
// Test getActiveNetworkInfo()
assertNull(mCm.getActiveNetworkInfo());
// Test getActiveNetwork()
assertNull(mCm.getActiveNetwork());
assertNull(mCm.getActiveNetworkForUid(Process.myUid()));
// Test getAllNetworks()
- assertEquals(0, mCm.getAllNetworks().length);
+ assertEmpty(mCm.getAllNetworks());
}
/**
@@ -906,6 +944,13 @@
// will fail. Failing here is much easier to debug.
assertTrue(mCm.isNetworkSupported(TYPE_WIFI));
assertTrue(mCm.isNetworkSupported(TYPE_MOBILE));
+ assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_MMS));
+ assertFalse(mCm.isNetworkSupported(TYPE_MOBILE_FOTA));
+
+ // Check that TYPE_ETHERNET is supported. Unlike the asserts above, which only validate our
+ // mocks, this assert exercises the ConnectivityService code path that ensures that
+ // TYPE_ETHERNET is supported if the ethernet service is running.
+ assertTrue(mCm.isNetworkSupported(TYPE_ETHERNET));
}
@SmallTest
@@ -920,7 +965,7 @@
mCellNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
- assertEquals(2, mCm.getAllNetworks().length);
+ assertLength(2, mCm.getAllNetworks());
assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
mCm.getAllNetworks()[1].equals(mCm.getActiveNetwork()));
assertTrue(mCm.getAllNetworks()[0].equals(mWiFiNetworkAgent.getNetwork()) ||
@@ -930,7 +975,7 @@
mWiFiNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
- assertEquals(2, mCm.getAllNetworks().length);
+ assertLength(2, mCm.getAllNetworks());
assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
mCm.getAllNetworks()[1].equals(mCm.getActiveNetwork()));
assertTrue(mCm.getAllNetworks()[0].equals(mCellNetworkAgent.getNetwork()) ||
@@ -938,9 +983,9 @@
// Test cellular linger timeout.
waitFor(mCellNetworkAgent.getDisconnectedCV());
waitForIdle();
- assertEquals(1, mCm.getAllNetworks().length);
+ assertLength(1, mCm.getAllNetworks());
verifyActiveNetwork(TRANSPORT_WIFI);
- assertEquals(1, mCm.getAllNetworks().length);
+ assertLength(1, mCm.getAllNetworks());
assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork());
// Test WiFi disconnect.
cv = waitForConnectivityBroadcasts(1);
@@ -1281,7 +1326,26 @@
return expectCallback(state, agent, TIMEOUT_MS);
}
- void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended, int timeoutMs) {
+ CallbackInfo expectCallbackLike(Predicate<CallbackInfo> fn) {
+ return expectCallbackLike(fn, TIMEOUT_MS);
+ }
+
+ CallbackInfo expectCallbackLike(Predicate<CallbackInfo> fn, int timeoutMs) {
+ int timeLeft = timeoutMs;
+ while (timeLeft > 0) {
+ long start = SystemClock.elapsedRealtime();
+ CallbackInfo info = nextCallback(timeLeft);
+ if (fn.test(info)) {
+ return info;
+ }
+ timeLeft -= (SystemClock.elapsedRealtime() - start);
+ }
+ fail("Did not receive expected callback after " + timeoutMs + "ms");
+ return null;
+ }
+
+ void expectAvailableCallbacks(
+ MockNetworkAgent agent, boolean expectSuspended, int timeoutMs) {
expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
if (expectSuspended) {
expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
@@ -1838,26 +1902,18 @@
@SmallTest
public void testNoMutableNetworkRequests() throws Exception {
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent("a"), 0);
- NetworkRequest.Builder builder = new NetworkRequest.Builder();
- builder.addCapability(NET_CAPABILITY_VALIDATED);
- try {
- mCm.requestNetwork(builder.build(), new NetworkCallback());
- fail();
- } catch (IllegalArgumentException expected) {}
- try {
- mCm.requestNetwork(builder.build(), pendingIntent);
- fail();
- } catch (IllegalArgumentException expected) {}
- builder = new NetworkRequest.Builder();
- builder.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
- try {
- mCm.requestNetwork(builder.build(), new NetworkCallback());
- fail();
- } catch (IllegalArgumentException expected) {}
- try {
- mCm.requestNetwork(builder.build(), pendingIntent);
- fail();
- } catch (IllegalArgumentException expected) {}
+ NetworkRequest request1 = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_VALIDATED)
+ .build();
+ NetworkRequest request2 = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL)
+ .build();
+
+ Class<IllegalArgumentException> expected = IllegalArgumentException.class;
+ assertException(() -> { mCm.requestNetwork(request1, new NetworkCallback()); }, expected);
+ assertException(() -> { mCm.requestNetwork(request1, pendingIntent); }, expected);
+ assertException(() -> { mCm.requestNetwork(request2, new NetworkCallback()); }, expected);
+ assertException(() -> { mCm.requestNetwork(request2, pendingIntent); }, expected);
}
@SmallTest
@@ -1869,7 +1925,7 @@
mCellNetworkAgent.connectWithoutInternet();
waitFor(cv);
waitForIdle();
- assertEquals(0, mCm.getAllNetworks().length);
+ assertEmpty(mCm.getAllNetworks());
verifyNoNetwork();
// Test bringing up validated WiFi.
@@ -2543,7 +2599,7 @@
assertTrue(testFactory.getMyStartRequested());
// Bring up cell data and check that the factory stops looking.
- assertEquals(1, mCm.getAllNetworks().length);
+ assertLength(1, mCm.getAllNetworks());
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
testFactory.expectAddRequests(2); // Because the cell request changes score twice.
mCellNetworkAgent.connect(true);
@@ -2554,7 +2610,7 @@
// Check that cell data stays up.
waitForIdle();
verifyActiveNetwork(TRANSPORT_WIFI);
- assertEquals(2, mCm.getAllNetworks().length);
+ assertLength(2, mCm.getAllNetworks());
// Turn off mobile data always on and expect the request to disappear...
testFactory.expectRemoveRequests(1);
@@ -2563,7 +2619,7 @@
// ... and cell data to be torn down.
cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
- assertEquals(1, mCm.getAllNetworks().length);
+ assertLength(1, mCm.getAllNetworks());
testFactory.unregister();
mCm.unregisterNetworkCallback(cellNetworkCallback);
@@ -2784,19 +2840,17 @@
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
- final int requestTimeoutMs = 100;
+ final int requestTimeoutMs = 50;
mCm.requestNetwork(nr, networkCallback, requestTimeoutMs);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- final int assertTimeoutMs = 150;
+ final int assertTimeoutMs = 100;
networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, assertTimeoutMs);
- sleepFor(20);
mWiFiNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- // pass timeout and validate that UNAVAILABLE is not called
- sleepFor(100);
+ // Validate that UNAVAILABLE is not called
networkCallback.assertNoCallback();
}
@@ -2823,24 +2877,20 @@
}
/**
- * Validate that when a network request is unregistered (cancelled) the time-out for that
- * request doesn't trigger the onUnavailable() callback.
+ * Validate that when a network request is unregistered (cancelled), no posterior event can
+ * trigger the callback.
*/
@SmallTest
- public void testTimedoutAfterUnregisteredNetworkRequest() {
+ public void testNoCallbackAfterUnregisteredNetworkRequest() {
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
final int timeoutMs = 10;
+
mCm.requestNetwork(nr, networkCallback, timeoutMs);
-
- // remove request
mCm.unregisterNetworkCallback(networkCallback);
-
- // pass timeout and validate that no callbacks
- // Note: doesn't validate that nothing called from CS since even if called the CM already
- // unregisters the callback and won't pass it through!
- sleepFor(15);
+ // Regardless of the timeout, unregistering the callback in ConnectivityManager ensures
+ // that this callback will not be called.
networkCallback.assertNoCallback();
// create a network satisfying request - validate that request not triggered
@@ -3259,12 +3309,229 @@
}
}
- /* test utilities */
- // TODO: eliminate all usages of sleepFor and replace by proper timeouts/waitForIdle.
- static private void sleepFor(int ms) {
- try {
- Thread.sleep(ms);
- } catch (InterruptedException e) {
+ @SmallTest
+ public void testNetworkInfoOfTypeNone() {
+ ConditionVariable broadcastCV = waitForConnectivityBroadcasts(1);
+
+ verifyNoNetwork();
+ MockNetworkAgent wifiAware = new MockNetworkAgent(TRANSPORT_WIFI_AWARE);
+ assertNull(mCm.getActiveNetworkInfo());
+
+ Network[] allNetworks = mCm.getAllNetworks();
+ assertLength(1, allNetworks);
+ Network network = allNetworks[0];
+ NetworkCapabilities capabilities = mCm.getNetworkCapabilities(network);
+ assertTrue(capabilities.hasTransport(TRANSPORT_WIFI_AWARE));
+
+ final NetworkRequest request =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI_AWARE).build();
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+
+ // Bring up wifi aware network.
+ wifiAware.connect(false, false);
+ callback.expectAvailableCallbacks(wifiAware);
+
+ assertNull(mCm.getActiveNetworkInfo());
+ assertNull(mCm.getActiveNetwork());
+ // TODO: getAllNetworkInfo is dirty and returns a non-empty array right from the start
+ // of this test. Fix it and uncomment the assert below.
+ //assertEmpty(mCm.getAllNetworkInfo());
+
+ // Disconnect wifi aware network.
+ wifiAware.disconnect();
+ callback.expectCallbackLike((info) -> info.state == CallbackState.LOST, TIMEOUT_MS);
+ mCm.unregisterNetworkCallback(callback);
+
+ verifyNoNetwork();
+ if (broadcastCV.block(10)) {
+ fail("expected no broadcast, but got CONNECTIVITY_ACTION broadcast");
}
}
+
+ @SmallTest
+ public void testDeprecatedAndUnsupportedOperations() throws Exception {
+ final int TYPE_NONE = ConnectivityManager.TYPE_NONE;
+ assertNull(mCm.getNetworkInfo(TYPE_NONE));
+ assertNull(mCm.getNetworkForType(TYPE_NONE));
+ assertNull(mCm.getLinkProperties(TYPE_NONE));
+ assertFalse(mCm.isNetworkSupported(TYPE_NONE));
+
+ assertException(() -> { mCm.networkCapabilitiesForType(TYPE_NONE); },
+ IllegalArgumentException.class);
+
+ Class<UnsupportedOperationException> unsupported = UnsupportedOperationException.class;
+ assertException(() -> { mCm.startUsingNetworkFeature(TYPE_WIFI, ""); }, unsupported);
+ assertException(() -> { mCm.stopUsingNetworkFeature(TYPE_WIFI, ""); }, unsupported);
+ // TODO: let test context have configuration application target sdk version
+ // and test that pre-M requesting for TYPE_NONE sends back APN_REQUEST_FAILED
+ assertException(() -> { mCm.startUsingNetworkFeature(TYPE_NONE, ""); }, unsupported);
+ assertException(() -> { mCm.stopUsingNetworkFeature(TYPE_NONE, ""); }, unsupported);
+ assertException(() -> { mCm.requestRouteToHostAddress(TYPE_NONE, null); }, unsupported);
+ }
+
+ @SmallTest
+ public void testLinkPropertiesEnsuresDirectlyConnectedRoutes() {
+ final NetworkRequest networkRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ final TestNetworkCallback networkCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(networkRequest, networkCallback);
+
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName("wlan0");
+ LinkAddress myIpv4Address = new LinkAddress("192.168.12.3/24");
+ RouteInfo myIpv4DefaultRoute = new RouteInfo((IpPrefix) null,
+ NetworkUtils.numericToInetAddress("192.168.12.1"), lp.getInterfaceName());
+ lp.addLinkAddress(myIpv4Address);
+ lp.addRoute(myIpv4DefaultRoute);
+
+ // Verify direct routes are added when network agent is first registered in
+ // ConnectivityService.
+ MockNetworkAgent networkAgent = new MockNetworkAgent(TRANSPORT_WIFI, lp);
+ networkAgent.connect(true);
+ networkCallback.expectCallback(CallbackState.AVAILABLE, networkAgent);
+ networkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, networkAgent);
+ CallbackInfo cbi = networkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ networkAgent);
+ networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, networkAgent);
+ networkCallback.assertNoCallback();
+ checkDirectlyConnectedRoutes(cbi.arg, Arrays.asList(myIpv4Address),
+ Arrays.asList(myIpv4DefaultRoute));
+ checkDirectlyConnectedRoutes(mCm.getLinkProperties(networkAgent.getNetwork()),
+ Arrays.asList(myIpv4Address), Arrays.asList(myIpv4DefaultRoute));
+
+ // Verify direct routes are added during subsequent link properties updates.
+ LinkProperties newLp = new LinkProperties(lp);
+ LinkAddress myIpv6Address1 = new LinkAddress("fe80::cafe/64");
+ LinkAddress myIpv6Address2 = new LinkAddress("2001:db8::2/64");
+ newLp.addLinkAddress(myIpv6Address1);
+ newLp.addLinkAddress(myIpv6Address2);
+ networkAgent.sendLinkProperties(newLp);
+ cbi = networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, networkAgent);
+ networkCallback.assertNoCallback();
+ checkDirectlyConnectedRoutes(cbi.arg,
+ Arrays.asList(myIpv4Address, myIpv6Address1, myIpv6Address2),
+ Arrays.asList(myIpv4DefaultRoute));
+ mCm.unregisterNetworkCallback(networkCallback);
+ }
+
+ private void checkDirectlyConnectedRoutes(Object callbackObj,
+ Collection<LinkAddress> linkAddresses, Collection<RouteInfo> otherRoutes) {
+ assertTrue(callbackObj instanceof LinkProperties);
+ LinkProperties lp = (LinkProperties) callbackObj;
+
+ Set<RouteInfo> expectedRoutes = new ArraySet<>();
+ expectedRoutes.addAll(otherRoutes);
+ for (LinkAddress address : linkAddresses) {
+ RouteInfo localRoute = new RouteInfo(address, null, lp.getInterfaceName());
+ // Duplicates in linkAddresses are considered failures
+ assertTrue(expectedRoutes.add(localRoute));
+ }
+ List<RouteInfo> observedRoutes = lp.getRoutes();
+ assertEquals(expectedRoutes.size(), observedRoutes.size());
+ assertTrue(observedRoutes.containsAll(expectedRoutes));
+ }
+
+ private static <T> void assertEmpty(T[] ts) {
+ int length = ts.length;
+ assertEquals("expected empty array, but length was " + length, 0, length);
+ }
+
+ private static <T> void assertLength(int expected, T[] got) {
+ int length = got.length;
+ assertEquals(String.format("expected array of length %s, but length was %s for %s",
+ expected, length, Arrays.toString(got)), expected, length);
+ }
+
+ private static <T> void assertException(Runnable block, Class<T> expected) {
+ try {
+ block.run();
+ fail("Expected exception of type " + expected);
+ } catch (Exception got) {
+ if (!got.getClass().equals(expected)) {
+ fail("Expected exception of type " + expected + " but got " + got);
+ }
+ return;
+ }
+ }
+
+ @SmallTest
+ public void testVpnNetworkMetered() {
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+
+ final NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ final TestNetworkCallback cellCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(cellRequest, cellCallback);
+
+ // Setup cellular
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ verifyActiveNetwork(TRANSPORT_CELLULAR);
+
+ // Verify meteredness of cellular
+ assertTrue(mCm.isActiveNetworkMetered());
+
+ // Setup Wifi
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ callback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ cellCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ verifyActiveNetwork(TRANSPORT_WIFI);
+
+ // Verify meteredness of WiFi
+ assertTrue(mCm.isActiveNetworkMetered());
+
+ // Verify that setting unmetered on Wifi changes ActiveNetworkMetered
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ callback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mWiFiNetworkAgent);
+ assertFalse(mCm.isActiveNetworkMetered());
+
+ // Setup VPN
+ final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ vpnNetworkAgent.connect(true);
+
+ Vpn mockVpn = mock(Vpn.class);
+ when(mockVpn.appliesToUid(anyInt())).thenReturn(true);
+ when(mockVpn.getNetId()).thenReturn(vpnNetworkAgent.getNetwork().netId);
+
+ Vpn oldVpn = mService.getVpn(UserHandle.myUserId());
+ mService.setVpn(UserHandle.myUserId(), mockVpn);
+ assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+ // Verify meteredness of VPN on default network
+ when(mockVpn.getUnderlyingNetworks()).thenReturn(null);
+ assertFalse(mCm.isActiveNetworkMetered());
+ assertFalse(mCm.isActiveNetworkMeteredForUid(Process.myUid()));
+
+ // Verify meteredness of VPN on unmetered wifi
+ when(mockVpn.getUnderlyingNetworks())
+ .thenReturn(new Network[] {mWiFiNetworkAgent.getNetwork()});
+ assertFalse(mCm.isActiveNetworkMetered());
+ assertFalse(mCm.isActiveNetworkMeteredForUid(Process.myUid()));
+
+ // Set WiFi as metered, then check to see that it has been updated on the VPN
+ mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+ callback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mWiFiNetworkAgent);
+ assertTrue(mCm.isActiveNetworkMetered());
+ assertTrue(mCm.isActiveNetworkMeteredForUid(Process.myUid()));
+
+ // Switch to cellular
+ when(mockVpn.getUnderlyingNetworks())
+ .thenReturn(new Network[] {mCellNetworkAgent.getNetwork()});
+ assertTrue(mCm.isActiveNetworkMetered());
+ assertTrue(mCm.isActiveNetworkMeteredForUid(Process.myUid()));
+
+ // Test unmetered cellular
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ cellCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
+ assertFalse(mCm.isActiveNetworkMetered());
+ assertFalse(mCm.isActiveNetworkMeteredForUid(Process.myUid()));
+
+ mService.setVpn(UserHandle.myUserId(), oldVpn);
+ mCm.unregisterNetworkCallback(callback);
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index d11565a..2624176 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity;
+import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
+import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
import static com.android.server.connectivity.MetricsTestUtil.aBool;
import static com.android.server.connectivity.MetricsTestUtil.aByteArray;
import static com.android.server.connectivity.MetricsTestUtil.aLong;
@@ -31,29 +33,41 @@
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.ETHERNET;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.MULTIPLE;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.WIFI;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import android.net.ConnectivityMetricsEvent;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfStats;
+import android.net.metrics.ConnectStats;
import android.net.metrics.DefaultNetworkEvent;
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpErrorEvent;
import android.net.metrics.DnsEvent;
+import android.net.metrics.DnsEvent;
import android.net.metrics.IpManagerEvent;
import android.net.metrics.IpReachabilityEvent;
import android.net.metrics.NetworkEvent;
import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
+import android.net.metrics.WakeupStats;
+import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
+
import java.util.Arrays;
import java.util.List;
-import junit.framework.TestCase;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
// TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
-public class IpConnectivityEventBuilderTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IpConnectivityEventBuilderTest {
- @SmallTest
+ @Test
public void testLinkLayerInferrence() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(IpReachabilityEvent.class),
@@ -182,7 +196,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testDefaultNetworkEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(DefaultNetworkEvent.class),
@@ -201,9 +215,14 @@
" time_ms: 1",
" transports: 0",
" default_network_event <",
+ " default_network_duration_ms: 0",
+ " final_score: 0",
+ " initial_score: 0",
+ " ip_support: 0",
" network_id <",
" network_id: 102",
" >",
+ " no_default_network_duration_ms: 0",
" previous_network_id <",
" network_id: 101",
" >",
@@ -218,7 +237,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testDhcpClientEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(DhcpClientEvent.class),
@@ -244,7 +263,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testDhcpErrorEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(DhcpErrorEvent.class),
@@ -269,7 +288,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testIpManagerEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(IpManagerEvent.class),
@@ -295,7 +314,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testIpReachabilityEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(IpReachabilityEvent.class),
@@ -319,7 +338,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testNetworkEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(NetworkEvent.class),
@@ -348,7 +367,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testValidationProbeEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(ValidationProbeEvent.class),
@@ -375,7 +394,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testApfProgramEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(ApfProgramEvent.class),
@@ -409,7 +428,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testApfStatsSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(ApfStats.class),
@@ -442,6 +461,8 @@
" program_updates_all: 7",
" program_updates_allowing_multicast: 3",
" received_ras: 10",
+ " total_packet_dropped: 0",
+ " total_packet_processed: 0",
" zero_lifetime_ras: 1",
" >",
">",
@@ -450,7 +471,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testRaEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(RaEvent.class),
@@ -483,11 +504,49 @@
verifySerialization(want, ev);
}
+ @Test
+ public void testWakeupStatsSerialization() {
+ WakeupStats stats = new WakeupStats("wlan0");
+ stats.totalWakeups = 14;
+ stats.applicationWakeups = 5;
+ stats.nonApplicationWakeups = 1;
+ stats.rootWakeups = 2;
+ stats.systemWakeups = 3;
+ stats.noUidWakeups = 3;
+
+ IpConnectivityEvent got = IpConnectivityEventBuilder.toProto(stats);
+ String want = String.join("\n",
+ "dropped_events: 0",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 4",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " wakeup_stats <",
+ " application_wakeups: 5",
+ " duration_sec: 0",
+ " no_uid_wakeups: 3",
+ " non_application_wakeups: 1",
+ " root_wakeups: 2",
+ " system_wakeups: 3",
+ " total_wakeups: 14",
+ " >",
+ ">",
+ "version: 2\n");
+
+ verifySerialization(want, got);
+ }
+
static void verifySerialization(String want, ConnectivityMetricsEvent... input) {
+ List<IpConnectivityEvent> protoInput =
+ IpConnectivityEventBuilder.toProto(Arrays.asList(input));
+ verifySerialization(want, protoInput.toArray(new IpConnectivityEvent[0]));
+ }
+
+ static void verifySerialization(String want, IpConnectivityEvent... input) {
try {
- List<IpConnectivityEvent> proto =
- IpConnectivityEventBuilder.toProto(Arrays.asList(input));
- byte[] got = IpConnectivityEventBuilder.serialize(0, proto);
+ byte[] got = IpConnectivityEventBuilder.serialize(0, Arrays.asList(input));
IpConnectivityLog log = IpConnectivityLog.parseFrom(got);
assertEquals(want, log.toString());
} catch (Exception e) {
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index e01469b..a395c48 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -224,6 +224,15 @@
dnsEvent(101, EVENT_GETADDRINFO, 0, 56);
dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 34);
+ // iface, uid
+ wakeupEvent("wlan0", 1000);
+ wakeupEvent("rmnet0", 10123);
+ wakeupEvent("wlan0", 1000);
+ wakeupEvent("rmnet0", 10008);
+ wakeupEvent("wlan0", -1);
+ wakeupEvent("wlan0", 10008);
+ wakeupEvent("rmnet0", 1000);
+
String want = String.join("\n",
"dropped_events: 0",
"events <",
@@ -256,9 +265,14 @@
" time_ms: 300",
" transports: 0",
" default_network_event <",
+ " default_network_duration_ms: 0",
+ " final_score: 0",
+ " initial_score: 0",
+ " ip_support: 0",
" network_id <",
" network_id: 102",
" >",
+ " no_default_network_duration_ms: 0",
" previous_network_id <",
" network_id: 101",
" >",
@@ -308,6 +322,8 @@
" program_updates_all: 7",
" program_updates_allowing_multicast: 3",
" received_ras: 10",
+ " total_packet_dropped: 0",
+ " total_packet_processed: 0",
" zero_lifetime_ras: 1",
" >",
">",
@@ -367,6 +383,10 @@
" event_types: 1",
" event_types: 1",
" event_types: 2",
+ " getaddrinfo_error_count: 0",
+ " getaddrinfo_query_count: 0",
+ " gethostbyname_error_count: 0",
+ " gethostbyname_query_count: 0",
" latencies_ms: 3456",
" latencies_ms: 45",
" latencies_ms: 638",
@@ -384,12 +404,48 @@
" dns_lookup_batch <",
" event_types: 1",
" event_types: 2",
+ " getaddrinfo_error_count: 0",
+ " getaddrinfo_query_count: 0",
+ " gethostbyname_error_count: 0",
+ " gethostbyname_query_count: 0",
" latencies_ms: 56",
" latencies_ms: 34",
" return_codes: 0",
" return_codes: 0",
" >",
">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 2",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " wakeup_stats <",
+ " application_wakeups: 2",
+ " duration_sec: 0",
+ " no_uid_wakeups: 0",
+ " non_application_wakeups: 0",
+ " root_wakeups: 0",
+ " system_wakeups: 1",
+ " total_wakeups: 3",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 4",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " wakeup_stats <",
+ " application_wakeups: 1",
+ " duration_sec: 0",
+ " no_uid_wakeups: 1",
+ " non_application_wakeups: 0",
+ " root_wakeups: 0",
+ " system_wakeups: 2",
+ " total_wakeups: 4",
+ " >",
+ ">",
"version: 2\n");
verifySerialization(want, getdump("flush"));
@@ -410,6 +466,11 @@
mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
}
+ void wakeupEvent(String iface, int uid) throws Exception {
+ String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
+ mNetdListener.onWakeupEvent(prefix, uid, uid, 0);
+ }
+
List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception {
ArgumentCaptor<ConnectivityMetricsEvent> captor =
ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
new file mode 100644
index 0000000..e3f46a4
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.net.ConnectivityManager;
+import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.os.Handler;
+import android.os.INetworkManagementService;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.ConnectivityService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class Nat464XlatTest {
+
+ static final String BASE_IFACE = "test0";
+ static final String STACKED_IFACE = "v4-test0";
+ static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29");
+
+ @Mock ConnectivityService mConnectivity;
+ @Mock INetworkManagementService mNms;
+ @Mock InterfaceConfiguration mConfig;
+ @Mock NetworkAgentInfo mNai;
+
+ TestLooper mLooper;
+ Handler mHandler;
+
+ Nat464Xlat makeNat464Xlat() {
+ return new Nat464Xlat(mNms, mNai);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mLooper = new TestLooper();
+ mHandler = new Handler(mLooper.getLooper());
+
+ MockitoAnnotations.initMocks(this);
+
+ mNai.linkProperties = new LinkProperties();
+ mNai.linkProperties.setInterfaceName(BASE_IFACE);
+ mNai.networkInfo = new NetworkInfo(null);
+ mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
+ when(mNai.connService()).thenReturn(mConnectivity);
+ when(mNai.handler()).thenReturn(mHandler);
+
+ when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig);
+ when(mConfig.getLinkAddress()).thenReturn(ADDR);
+ }
+
+ @Test
+ public void testNormalStartAndStop() throws Exception {
+ Nat464Xlat nat = makeNat464Xlat();
+ ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
+
+ // ConnectivityService starts clat.
+ nat.start();
+
+ verify(mNms).registerObserver(eq(nat));
+ verify(mNms).startClatd(eq(BASE_IFACE));
+
+ // Stacked interface up notification arrives.
+ nat.interfaceLinkStateChanged(STACKED_IFACE, true);
+ mLooper.dispatchNext();
+
+ verify(mNms).getInterfaceConfig(eq(STACKED_IFACE));
+ verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(false));
+ verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture());
+ assertFalse(c.getValue().getStackedLinks().isEmpty());
+ assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
+ assertRunning(nat);
+
+ // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...).
+ nat.stop();
+
+ verify(mNms).stopClatd(eq(BASE_IFACE));
+ verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(true));
+
+ // Stacked interface removed notification arrives.
+ nat.interfaceRemoved(STACKED_IFACE);
+ mLooper.dispatchNext();
+
+ verify(mNms).unregisterObserver(eq(nat));
+ verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
+ assertTrue(c.getValue().getStackedLinks().isEmpty());
+ assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
+ assertIdle(nat);
+
+ verifyNoMoreInteractions(mNms, mConnectivity);
+ }
+
+ @Test
+ public void testClatdCrashWhileRunning() throws Exception {
+ Nat464Xlat nat = makeNat464Xlat();
+ ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
+
+ // ConnectivityService starts clat.
+ nat.start();
+
+ verify(mNms).registerObserver(eq(nat));
+ verify(mNms).startClatd(eq(BASE_IFACE));
+
+ // Stacked interface up notification arrives.
+ nat.interfaceLinkStateChanged(STACKED_IFACE, true);
+ mLooper.dispatchNext();
+
+ verify(mNms).getInterfaceConfig(eq(STACKED_IFACE));
+ verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(false));
+ verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
+ assertFalse(c.getValue().getStackedLinks().isEmpty());
+ assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
+ assertRunning(nat);
+
+ // Stacked interface removed notification arrives (clatd crashed, ...).
+ nat.interfaceRemoved(STACKED_IFACE);
+ mLooper.dispatchNext();
+
+ verify(mNms).unregisterObserver(eq(nat));
+ verify(mNms).stopClatd(eq(BASE_IFACE));
+ verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(true));
+ verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
+ assertTrue(c.getValue().getStackedLinks().isEmpty());
+ assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
+ assertIdle(nat);
+
+ // ConnectivityService stops clat: no-op.
+ nat.stop();
+
+ verifyNoMoreInteractions(mNms, mConnectivity);
+ }
+
+ @Test
+ public void testStopBeforeClatdStarts() throws Exception {
+ Nat464Xlat nat = makeNat464Xlat();
+
+ // ConnectivityService starts clat.
+ nat.start();
+
+ verify(mNms).registerObserver(eq(nat));
+ verify(mNms).startClatd(eq(BASE_IFACE));
+
+ // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
+ nat.stop();
+
+ verify(mNms).unregisterObserver(eq(nat));
+ verify(mNms).stopClatd(eq(BASE_IFACE));
+ assertIdle(nat);
+
+ // In-flight interface up notification arrives: no-op
+ nat.interfaceLinkStateChanged(STACKED_IFACE, true);
+ mLooper.dispatchNext();
+
+
+ // Interface removed notification arrives after stopClatd() takes effect: no-op.
+ nat.interfaceRemoved(STACKED_IFACE);
+ mLooper.dispatchNext();
+
+ assertIdle(nat);
+
+ verifyNoMoreInteractions(mNms, mConnectivity);
+ }
+
+ @Test
+ public void testStopAndClatdNeverStarts() throws Exception {
+ Nat464Xlat nat = makeNat464Xlat();
+
+ // ConnectivityService starts clat.
+ nat.start();
+
+ verify(mNms).registerObserver(eq(nat));
+ verify(mNms).startClatd(eq(BASE_IFACE));
+
+ // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
+ nat.stop();
+
+ verify(mNms).unregisterObserver(eq(nat));
+ verify(mNms).stopClatd(eq(BASE_IFACE));
+ assertIdle(nat);
+
+ verifyNoMoreInteractions(mNms, mConnectivity);
+ }
+
+ static void assertIdle(Nat464Xlat nat) {
+ assertTrue("Nat464Xlat was not IDLE", !nat.isStarted());
+ }
+
+ static void assertRunning(Nat464Xlat nat) {
+ assertTrue("Nat464Xlat was not RUNNING", nat.isRunning());
+ }
+}
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index f98ab3d..6723601 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -19,6 +19,7 @@
import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
@@ -37,9 +38,11 @@
import android.system.OsConstants;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Base64;
+
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.DNSLookupBatch;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
+
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -47,6 +50,7 @@
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -75,6 +79,118 @@
}
@Test
+ public void testWakeupEventLogging() throws Exception {
+ final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;
+
+ // Assert no events
+ String[] events1 = listNetdEvent();
+ assertEquals(new String[]{""}, events1);
+
+ long now = System.currentTimeMillis();
+ String prefix = "iface:wlan0";
+ int[] uids = { 10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004 };
+ for (int uid : uids) {
+ mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
+ }
+
+ String[] events2 = listNetdEvent();
+ int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line
+ assertEquals(expectedLength2, events2.length);
+ assertContains(events2[0], "WakeupStats");
+ assertContains(events2[0], "wlan0");
+ for (int i = 0; i < uids.length; i++) {
+ String got = events2[i+1];
+ assertContains(got, "WakeupEvent");
+ assertContains(got, "wlan0");
+ assertContains(got, "uid: " + uids[i]);
+ }
+
+ int uid = 20000;
+ for (int i = 0; i < BUFFER_LENGTH * 2; i++) {
+ long ts = now + 10;
+ mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, ts);
+ }
+
+ String[] events3 = listNetdEvent();
+ int expectedLength3 = BUFFER_LENGTH + 1; // +1 for the WakeupStats line
+ assertEquals(expectedLength3, events3.length);
+ assertContains(events2[0], "WakeupStats");
+ assertContains(events2[0], "wlan0");
+ for (int i = 1; i < expectedLength3; i++) {
+ String got = events3[i];
+ assertContains(got, "WakeupEvent");
+ assertContains(got, "wlan0");
+ assertContains(got, "uid: " + uid);
+ }
+
+ uid = 45678;
+ mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
+
+ String[] events4 = listNetdEvent();
+ String lastEvent = events4[events4.length - 1];
+ assertContains(lastEvent, "WakeupEvent");
+ assertContains(lastEvent, "wlan0");
+ assertContains(lastEvent, "uid: " + uid);
+ }
+
+ @Test
+ public void testWakeupStatsLogging() throws Exception {
+ wakeupEvent("wlan0", 1000);
+ wakeupEvent("rmnet0", 10123);
+ wakeupEvent("wlan0", 1000);
+ wakeupEvent("rmnet0", 10008);
+ wakeupEvent("wlan0", -1);
+ wakeupEvent("wlan0", 10008);
+ wakeupEvent("rmnet0", 1000);
+ wakeupEvent("wlan0", 10004);
+ wakeupEvent("wlan0", 1000);
+ wakeupEvent("wlan0", 0);
+ wakeupEvent("wlan0", -1);
+ wakeupEvent("rmnet0", 10052);
+ wakeupEvent("wlan0", 0);
+ wakeupEvent("rmnet0", 1000);
+ wakeupEvent("wlan0", 1010);
+
+ String got = flushStatistics();
+ String want = String.join("\n",
+ "dropped_events: 0",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 2",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " wakeup_stats <",
+ " application_wakeups: 3",
+ " duration_sec: 0",
+ " no_uid_wakeups: 0",
+ " non_application_wakeups: 0",
+ " root_wakeups: 0",
+ " system_wakeups: 2",
+ " total_wakeups: 5",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 4",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " wakeup_stats <",
+ " application_wakeups: 2",
+ " duration_sec: 0",
+ " no_uid_wakeups: 2",
+ " non_application_wakeups: 1",
+ " root_wakeups: 2",
+ " system_wakeups: 3",
+ " total_wakeups: 10",
+ " >",
+ ">",
+ "version: 2\n");
+ assertEquals(want, got);
+ }
+
+ @Test
public void testDnsLogging() throws Exception {
asyncDump(100);
@@ -111,6 +227,10 @@
" event_types: 1",
" event_types: 2",
" event_types: 2",
+ " getaddrinfo_error_count: 0",
+ " getaddrinfo_query_count: 0",
+ " gethostbyname_error_count: 0",
+ " gethostbyname_query_count: 0",
" latencies_ms: 3456",
" latencies_ms: 267",
" latencies_ms: 1230",
@@ -142,6 +262,10 @@
" event_types: 2",
" event_types: 1",
" event_types: 1",
+ " getaddrinfo_error_count: 0",
+ " getaddrinfo_query_count: 0",
+ " gethostbyname_error_count: 0",
+ " gethostbyname_query_count: 0",
" latencies_ms: 56",
" latencies_ms: 78",
" latencies_ms: 14",
@@ -289,6 +413,11 @@
mNetdEventListenerService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
}
+ void wakeupEvent(String iface, int uid) throws Exception {
+ String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
+ mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, 0);
+ }
+
void asyncDump(long durationMs) throws Exception {
final long stop = System.currentTimeMillis() + durationMs;
final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
@@ -321,4 +450,15 @@
}
return log.toString();
}
+
+ String[] listNetdEvent() throws Exception {
+ StringWriter buffer = new StringWriter();
+ PrintWriter writer = new PrintWriter(buffer);
+ mNetdEventListenerService.list(writer);
+ return buffer.toString().split("\\n");
+ }
+
+ static void assertContains(String got, String want) {
+ assertTrue(got + " did not contain \"" + want + "\"", got.contains(want));
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 506d9e5..296cb76 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -27,13 +27,16 @@
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.UidRange;
-import android.os.Build;
+import android.net.VpnService;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.UserHandle;
@@ -45,22 +48,22 @@
import com.android.internal.net.VpnConfig;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Set;
-
import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
/**
* Tests for {@link Vpn}.
*
* Build, install and run with:
- * runtest --path src/com/android/server/connectivity/VpnTest.java
+ * runtest --path java/com/android/server/connectivity/VpnTest.java
*/
public class VpnTest extends AndroidTestCase {
private static final String TAG = "VpnTest";
@@ -116,7 +119,7 @@
// Used by {@link Notification.Builder}
ApplicationInfo applicationInfo = new ApplicationInfo();
- applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+ applicationInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
when(mContext.getApplicationInfo()).thenReturn(applicationInfo);
doNothing().when(mNetService).registerObserver(any());
@@ -315,6 +318,40 @@
}
@SmallTest
+ public void testIsAlwaysOnPackageSupported() throws Exception {
+ final Vpn vpn = createVpn(primaryUser.id);
+
+ ApplicationInfo appInfo = new ApplicationInfo();
+ when(mPackageManager.getApplicationInfoAsUser(eq(PKGS[0]), anyInt(), eq(primaryUser.id)))
+ .thenReturn(appInfo);
+
+ ServiceInfo svcInfo = new ServiceInfo();
+ ResolveInfo resInfo = new ResolveInfo();
+ resInfo.serviceInfo = svcInfo;
+ when(mPackageManager.queryIntentServicesAsUser(any(), eq(PackageManager.GET_META_DATA),
+ eq(primaryUser.id)))
+ .thenReturn(Collections.singletonList(resInfo));
+
+ // null package name should return false
+ assertFalse(vpn.isAlwaysOnPackageSupported(null));
+
+ // Pre-N apps are not supported
+ appInfo.targetSdkVersion = VERSION_CODES.M;
+ assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+
+ // N+ apps are supported by default
+ appInfo.targetSdkVersion = VERSION_CODES.N;
+ assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+
+ // Apps that opt out explicitly are not supported
+ appInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
+ Bundle metaData = new Bundle();
+ metaData.putBoolean(VpnService.SERVICE_META_DATA_SUPPORTS_ALWAYS_ON, false);
+ svcInfo.metaData = metaData;
+ assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+ }
+
+ @SmallTest
public void testNotificationShownForAlwaysOnApp() {
final UserHandle userHandle = UserHandle.of(primaryUser.id);
final Vpn vpn = createVpn(primaryUser.id);
diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
index 2a32b73..9c10264 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
@@ -17,26 +17,39 @@
package com.android.server.net;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
+import static android.net.NetworkStatsHistory.FIELD_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
+import static android.os.Process.myUid;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static com.android.server.net.NetworkStatsCollection.multiplySafe;
+
import android.content.res.Resources;
+import android.net.ConnectivityManager;
import android.net.NetworkIdentity;
import android.net.NetworkStats;
+import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.os.Process;
import android.os.UserHandle;
+import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyManager;
-import android.support.test.filters.SmallTest;
import android.test.AndroidTestCase;
import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.format.DateUtils;
+import android.util.RecurrenceRule;
import com.android.frameworks.tests.net.R;
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
@@ -44,9 +57,12 @@
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
-
-import libcore.io.IoUtils;
-import libcore.io.Streams;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.List;
/**
* Tests for {@link NetworkStatsCollection}.
@@ -57,14 +73,31 @@
private static final String TEST_FILE = "test.bin";
private static final String TEST_IMSI = "310260000000000";
+ private static final long TIME_A = 1326088800000L; // UTC: Monday 9th January 2012 06:00:00 AM
+ private static final long TIME_B = 1326110400000L; // UTC: Monday 9th January 2012 12:00:00 PM
+ private static final long TIME_C = 1326132000000L; // UTC: Monday 9th January 2012 06:00:00 PM
+
+ private static Clock sOriginalClock;
+
@Override
public void setUp() throws Exception {
super.setUp();
+ sOriginalClock = RecurrenceRule.sClock;
// ignore any device overlay while testing
NetworkTemplate.forceAllNetworkTypes();
}
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ RecurrenceRule.sClock = sOriginalClock;
+ }
+
+ private void setClock(Instant instant) {
+ RecurrenceRule.sClock = Clock.fixed(instant, ZoneId.systemDefault());
+ }
+
public void testReadLegacyNetwork() throws Exception {
final File testFile = new File(getContext().getFilesDir(), TEST_FILE);
stageFile(R.raw.netstats_v1, testFile);
@@ -198,11 +231,11 @@
collection.getRelevantUids(NetworkStatsAccess.Level.DEVICE));
// Verify security check in getHistory.
- assertNotNull(collection.getHistory(buildTemplateMobileAll(TEST_IMSI), myUid, SET_DEFAULT,
- TAG_NONE, 0, NetworkStatsAccess.Level.DEFAULT));
+ assertNotNull(collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null, myUid, SET_DEFAULT,
+ TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid));
try {
- collection.getHistory(buildTemplateMobileAll(TEST_IMSI), otherUidInSameUser,
- SET_DEFAULT, TAG_NONE, 0, NetworkStatsAccess.Level.DEFAULT);
+ collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null, otherUidInSameUser,
+ SET_DEFAULT, TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid);
fail("Should have thrown SecurityException for accessing different UID");
} catch (SecurityException e) {
// expected
@@ -217,6 +250,257 @@
0, NetworkStatsAccess.Level.DEVICE);
}
+ public void testAugmentPlan() throws Exception {
+ final File testFile = new File(getContext().getFilesDir(), TEST_FILE);
+ stageFile(R.raw.netstats_v1, testFile);
+
+ final NetworkStatsCollection emptyCollection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
+ final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
+ collection.readLegacyNetwork(testFile);
+
+ // We're in the future, but not that far off
+ setClock(Instant.parse("2012-06-01T00:00:00.00Z"));
+
+ // Test a bunch of plans that should result in no augmentation
+ final List<SubscriptionPlan> plans = new ArrayList<>();
+
+ // No plan
+ plans.add(null);
+ // No usage anchor
+ plans.add(SubscriptionPlan.Builder
+ .createRecurringMonthly(ZonedDateTime.parse("2011-01-14T00:00:00.00Z")).build());
+ // Usage anchor far in past
+ plans.add(SubscriptionPlan.Builder
+ .createRecurringMonthly(ZonedDateTime.parse("2011-01-14T00:00:00.00Z"))
+ .setDataUsage(1000L, TIME_A - DateUtils.YEAR_IN_MILLIS).build());
+ // Usage anchor far in future
+ plans.add(SubscriptionPlan.Builder
+ .createRecurringMonthly(ZonedDateTime.parse("2011-01-14T00:00:00.00Z"))
+ .setDataUsage(1000L, TIME_A + DateUtils.YEAR_IN_MILLIS).build());
+ // Usage anchor near but outside cycle
+ plans.add(SubscriptionPlan.Builder
+ .createNonrecurring(ZonedDateTime.parse("2012-01-09T09:00:00.00Z"),
+ ZonedDateTime.parse("2012-01-09T15:00:00.00Z"))
+ .setDataUsage(1000L, TIME_C).build());
+
+ for (SubscriptionPlan plan : plans) {
+ int i;
+ NetworkStatsHistory history;
+
+ // Empty collection should be untouched
+ history = getHistory(emptyCollection, plan, TIME_A, TIME_C);
+ assertEquals(0L, history.getTotalBytes());
+
+ // Normal collection should be untouched
+ history = getHistory(collection, plan, TIME_A, TIME_C); i = 0;
+ assertEntry(100647, 197, 23649, 185, history.getValues(i++, null));
+ assertEntry(100647, 196, 23648, 185, history.getValues(i++, null));
+ assertEntry(18323, 76, 15032, 76, history.getValues(i++, null));
+ assertEntry(18322, 75, 15031, 75, history.getValues(i++, null));
+ assertEntry(527798, 761, 78570, 652, history.getValues(i++, null));
+ assertEntry(527797, 760, 78570, 651, history.getValues(i++, null));
+ assertEntry(10747, 50, 16838, 55, history.getValues(i++, null));
+ assertEntry(10747, 49, 16838, 54, history.getValues(i++, null));
+ assertEntry(89191, 151, 18021, 140, history.getValues(i++, null));
+ assertEntry(89190, 150, 18020, 139, history.getValues(i++, null));
+ assertEntry(3821, 22, 4525, 26, history.getValues(i++, null));
+ assertEntry(3820, 22, 4524, 26, history.getValues(i++, null));
+ assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
+ assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
+ assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
+ assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
+ assertEntry(113914, 174, 18364, 157, history.getValues(i++, null));
+ assertEntry(113913, 173, 18364, 157, history.getValues(i++, null));
+ assertEntry(11378, 49, 9261, 49, history.getValues(i++, null));
+ assertEntry(11377, 48, 9261, 49, history.getValues(i++, null));
+ assertEntry(201765, 328, 41808, 291, history.getValues(i++, null));
+ assertEntry(201765, 328, 41807, 290, history.getValues(i++, null));
+ assertEntry(106106, 218, 39917, 201, history.getValues(i++, null));
+ assertEntry(106105, 217, 39917, 201, history.getValues(i++, null));
+ assertEquals(history.size(), i);
+
+ // Slice from middle should be untouched
+ history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
+ TIME_B + HOUR_IN_MILLIS); i = 0;
+ assertEntry(3821, 22, 4525, 26, history.getValues(i++, null));
+ assertEntry(3820, 22, 4524, 26, history.getValues(i++, null));
+ assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
+ assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
+ assertEquals(history.size(), i);
+ }
+
+ // Lower anchor in the middle of plan
+ {
+ int i;
+ NetworkStatsHistory history;
+
+ final SubscriptionPlan plan = SubscriptionPlan.Builder
+ .createNonrecurring(ZonedDateTime.parse("2012-01-09T09:00:00.00Z"),
+ ZonedDateTime.parse("2012-01-09T15:00:00.00Z"))
+ .setDataUsage(200000L, TIME_B).build();
+
+ // Empty collection should be augmented
+ history = getHistory(emptyCollection, plan, TIME_A, TIME_C);
+ assertEquals(200000L, history.getTotalBytes());
+
+ // Normal collection should be augmented
+ history = getHistory(collection, plan, TIME_A, TIME_C); i = 0;
+ assertEntry(100647, 197, 23649, 185, history.getValues(i++, null));
+ assertEntry(100647, 196, 23648, 185, history.getValues(i++, null));
+ assertEntry(18323, 76, 15032, 76, history.getValues(i++, null));
+ assertEntry(18322, 75, 15031, 75, history.getValues(i++, null));
+ assertEntry(527798, 761, 78570, 652, history.getValues(i++, null));
+ assertEntry(527797, 760, 78570, 651, history.getValues(i++, null));
+ // Cycle point; start data normalization
+ assertEntry(7507, 0, 11763, 0, history.getValues(i++, null));
+ assertEntry(7507, 0, 11763, 0, history.getValues(i++, null));
+ assertEntry(62309, 0, 12589, 0, history.getValues(i++, null));
+ assertEntry(62309, 0, 12588, 0, history.getValues(i++, null));
+ assertEntry(2669, 0, 3161, 0, history.getValues(i++, null));
+ assertEntry(2668, 0, 3160, 0, history.getValues(i++, null));
+ // Anchor point; end data normalization
+ assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
+ assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
+ assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
+ assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
+ assertEntry(113914, 174, 18364, 157, history.getValues(i++, null));
+ assertEntry(113913, 173, 18364, 157, history.getValues(i++, null));
+ // Cycle point
+ assertEntry(11378, 49, 9261, 49, history.getValues(i++, null));
+ assertEntry(11377, 48, 9261, 49, history.getValues(i++, null));
+ assertEntry(201765, 328, 41808, 291, history.getValues(i++, null));
+ assertEntry(201765, 328, 41807, 290, history.getValues(i++, null));
+ assertEntry(106106, 218, 39917, 201, history.getValues(i++, null));
+ assertEntry(106105, 217, 39917, 201, history.getValues(i++, null));
+ assertEquals(history.size(), i);
+
+ // Slice from middle should be augmented
+ history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
+ TIME_B + HOUR_IN_MILLIS); i = 0;
+ assertEntry(2669, 0, 3161, 0, history.getValues(i++, null));
+ assertEntry(2668, 0, 3160, 0, history.getValues(i++, null));
+ assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
+ assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
+ assertEquals(history.size(), i);
+ }
+
+ // Higher anchor in the middle of plan
+ {
+ int i;
+ NetworkStatsHistory history;
+
+ final SubscriptionPlan plan = SubscriptionPlan.Builder
+ .createNonrecurring(ZonedDateTime.parse("2012-01-09T09:00:00.00Z"),
+ ZonedDateTime.parse("2012-01-09T15:00:00.00Z"))
+ .setDataUsage(400000L, TIME_B + MINUTE_IN_MILLIS).build();
+
+ // Empty collection should be augmented
+ history = getHistory(emptyCollection, plan, TIME_A, TIME_C);
+ assertEquals(400000L, history.getTotalBytes());
+
+ // Normal collection should be augmented
+ history = getHistory(collection, plan, TIME_A, TIME_C); i = 0;
+ assertEntry(100647, 197, 23649, 185, history.getValues(i++, null));
+ assertEntry(100647, 196, 23648, 185, history.getValues(i++, null));
+ assertEntry(18323, 76, 15032, 76, history.getValues(i++, null));
+ assertEntry(18322, 75, 15031, 75, history.getValues(i++, null));
+ assertEntry(527798, 761, 78570, 652, history.getValues(i++, null));
+ assertEntry(527797, 760, 78570, 651, history.getValues(i++, null));
+ // Cycle point; start data normalization
+ assertEntry(15015, 0, 23526, 0, history.getValues(i++, null));
+ assertEntry(15015, 0, 23526, 0, history.getValues(i++, null));
+ assertEntry(124619, 0, 25179, 0, history.getValues(i++, null));
+ assertEntry(124618, 0, 25177, 0, history.getValues(i++, null));
+ assertEntry(5338, 0, 6322, 0, history.getValues(i++, null));
+ assertEntry(5337, 0, 6320, 0, history.getValues(i++, null));
+ // Anchor point; end data normalization
+ assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
+ assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
+ assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
+ assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
+ assertEntry(113914, 174, 18364, 157, history.getValues(i++, null));
+ assertEntry(113913, 173, 18364, 157, history.getValues(i++, null));
+ // Cycle point
+ assertEntry(11378, 49, 9261, 49, history.getValues(i++, null));
+ assertEntry(11377, 48, 9261, 49, history.getValues(i++, null));
+ assertEntry(201765, 328, 41808, 291, history.getValues(i++, null));
+ assertEntry(201765, 328, 41807, 290, history.getValues(i++, null));
+ assertEntry(106106, 218, 39917, 201, history.getValues(i++, null));
+ assertEntry(106105, 217, 39917, 201, history.getValues(i++, null));
+
+ // Slice from middle should be augmented
+ history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
+ TIME_B + HOUR_IN_MILLIS); i = 0;
+ assertEntry(5338, 0, 6322, 0, history.getValues(i++, null));
+ assertEntry(5337, 0, 6320, 0, history.getValues(i++, null));
+ assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
+ assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
+ assertEquals(history.size(), i);
+ }
+ }
+
+ public void testAugmentPlanGigantic() throws Exception {
+ // We're in the future, but not that far off
+ setClock(Instant.parse("2012-06-01T00:00:00.00Z"));
+
+ // Create a simple history with a ton of measured usage
+ final NetworkStatsCollection large = new NetworkStatsCollection(HOUR_IN_MILLIS);
+ final NetworkIdentitySet ident = new NetworkIdentitySet();
+ ident.add(new NetworkIdentity(ConnectivityManager.TYPE_MOBILE, -1, TEST_IMSI, null,
+ false, true));
+ large.recordData(ident, UID_ALL, SET_ALL, TAG_NONE, TIME_A, TIME_B,
+ new NetworkStats.Entry(12_730_893_164L, 1, 0, 0, 0));
+
+ // Verify untouched total
+ assertEquals(12_730_893_164L, getHistory(large, null, TIME_A, TIME_C).getTotalBytes());
+
+ // Verify anchor that might cause overflows
+ final SubscriptionPlan plan = SubscriptionPlan.Builder
+ .createRecurringMonthly(ZonedDateTime.parse("2012-01-09T00:00:00.00Z"))
+ .setDataUsage(4_939_212_390L, TIME_B).build();
+ assertEquals(4_939_212_386L, getHistory(large, plan, TIME_A, TIME_C).getTotalBytes());
+ }
+
+ public void testRounding() throws Exception {
+ final NetworkStatsCollection coll = new NetworkStatsCollection(HOUR_IN_MILLIS);
+
+ // Special values should remain unchanged
+ for (long time : new long[] {
+ Long.MIN_VALUE, Long.MAX_VALUE, SubscriptionPlan.TIME_UNKNOWN
+ }) {
+ assertEquals(time, coll.roundUp(time));
+ assertEquals(time, coll.roundDown(time));
+ }
+
+ assertEquals(TIME_A, coll.roundUp(TIME_A));
+ assertEquals(TIME_A, coll.roundDown(TIME_A));
+
+ assertEquals(TIME_A + HOUR_IN_MILLIS, coll.roundUp(TIME_A + 1));
+ assertEquals(TIME_A, coll.roundDown(TIME_A + 1));
+
+ assertEquals(TIME_A, coll.roundUp(TIME_A - 1));
+ assertEquals(TIME_A - HOUR_IN_MILLIS, coll.roundDown(TIME_A - 1));
+ }
+
+ public void testMultiplySafe() {
+ assertEquals(25, multiplySafe(50, 1, 2));
+ assertEquals(100, multiplySafe(50, 2, 1));
+
+ assertEquals(-10, multiplySafe(30, -1, 3));
+ assertEquals(0, multiplySafe(30, 0, 3));
+ assertEquals(10, multiplySafe(30, 1, 3));
+ assertEquals(20, multiplySafe(30, 2, 3));
+ assertEquals(30, multiplySafe(30, 3, 3));
+ assertEquals(40, multiplySafe(30, 4, 3));
+
+ assertEquals(100_000_000_000L,
+ multiplySafe(300_000_000_000L, 10_000_000_000L, 30_000_000_000L));
+ assertEquals(100_000_000_010L,
+ multiplySafe(300_000_000_000L, 10_000_000_001L, 30_000_000_000L));
+ assertEquals(823_202_048L,
+ multiplySafe(4_939_212_288L, 2_121_815_528L, 12_730_893_165L));
+ }
+
/**
* Copy a {@link Resources#openRawResource(int)} into {@link File} for
* testing purposes.
@@ -235,28 +519,50 @@
}
}
+ private static NetworkStatsHistory getHistory(NetworkStatsCollection collection,
+ SubscriptionPlan augmentPlan, long start, long end) {
+ return collection.getHistory(buildTemplateMobileAll(TEST_IMSI), augmentPlan, UID_ALL,
+ SET_ALL, TAG_NONE, FIELD_ALL, start, end, NetworkStatsAccess.Level.DEVICE, myUid());
+ }
+
private static void assertSummaryTotal(NetworkStatsCollection collection,
NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets,
@NetworkStatsAccess.Level int accessLevel) {
- final NetworkStats.Entry entry = collection.getSummary(
- template, Long.MIN_VALUE, Long.MAX_VALUE, accessLevel)
+ final NetworkStats.Entry actual = collection.getSummary(
+ template, Long.MIN_VALUE, Long.MAX_VALUE, accessLevel, myUid())
.getTotal(null);
- assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
+ assertEntry(rxBytes, rxPackets, txBytes, txPackets, actual);
}
private static void assertSummaryTotalIncludingTags(NetworkStatsCollection collection,
NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) {
- final NetworkStats.Entry entry = collection.getSummary(
- template, Long.MIN_VALUE, Long.MAX_VALUE, NetworkStatsAccess.Level.DEVICE)
+ final NetworkStats.Entry actual = collection.getSummary(
+ template, Long.MIN_VALUE, Long.MAX_VALUE, NetworkStatsAccess.Level.DEVICE, myUid())
.getTotalIncludingTags(null);
- assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
+ assertEntry(rxBytes, rxPackets, txBytes, txPackets, actual);
}
- private static void assertEntry(
- NetworkStats.Entry entry, long rxBytes, long rxPackets, long txBytes, long txPackets) {
- assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
- assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
- assertEquals("unexpected txBytes", txBytes, entry.txBytes);
- assertEquals("unexpected txPackets", txPackets, entry.txPackets);
+ private static void assertEntry(long rxBytes, long rxPackets, long txBytes, long txPackets,
+ NetworkStats.Entry actual) {
+ assertEntry(new NetworkStats.Entry(rxBytes, rxPackets, txBytes, txPackets, 0L), actual);
+ }
+
+ private static void assertEntry(long rxBytes, long rxPackets, long txBytes, long txPackets,
+ NetworkStatsHistory.Entry actual) {
+ assertEntry(new NetworkStats.Entry(rxBytes, rxPackets, txBytes, txPackets, 0L), actual);
+ }
+
+ private static void assertEntry(NetworkStats.Entry expected,
+ NetworkStatsHistory.Entry actual) {
+ assertEntry(expected, new NetworkStats.Entry(actual.rxBytes, actual.rxPackets,
+ actual.txBytes, actual.txPackets, 0L));
+ }
+
+ private static void assertEntry(NetworkStats.Entry expected,
+ NetworkStats.Entry actual) {
+ assertEquals("unexpected rxBytes", expected.rxBytes, actual.rxBytes);
+ assertEquals("unexpected rxPackets", expected.rxPackets, actual.rxPackets);
+ assertEquals("unexpected txBytes", expected.txBytes, actual.txBytes);
+ assertEquals("unexpected txPackets", expected.txPackets, actual.txPackets);
}
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
index 92dcdac..2be5dae 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -27,6 +27,8 @@
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static com.android.internal.util.TestUtils.waitForIdleHandler;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -56,7 +58,6 @@
import com.android.internal.net.VpnInfo;
import com.android.server.net.NetworkStatsService;
-import com.android.server.net.NetworkStatsServiceTest.IdleableHandlerThread;
import com.android.server.net.NetworkStatsServiceTest.LatchedHandler;
import java.util.ArrayList;
@@ -102,7 +103,7 @@
private long mElapsedRealtime;
- private IdleableHandlerThread mObserverHandlerThread;
+ private HandlerThread mObserverHandlerThread;
private Handler mObserverNoopHandler;
private LatchedHandler mHandler;
@@ -118,7 +119,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mObserverHandlerThread = new IdleableHandlerThread("HandlerThread");
+ mObserverHandlerThread = new HandlerThread("HandlerThread");
mObserverHandlerThread.start();
final Looper observerLooper = mObserverHandlerThread.getLooper();
mStatsObservers = new NetworkStatsObservers() {
@@ -319,7 +320,7 @@
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
VPN_INFO, TEST_START);
waitForObserverToIdle();
- assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.mLastMessageType);
+ assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType);
}
@Test
@@ -356,7 +357,7 @@
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
VPN_INFO, TEST_START);
waitForObserverToIdle();
- assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.mLastMessageType);
+ assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType);
}
@Test
@@ -429,7 +430,7 @@
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
VPN_INFO, TEST_START);
waitForObserverToIdle();
- assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.mLastMessageType);
+ assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType);
}
@Test
@@ -470,19 +471,7 @@
}
private void waitForObserverToIdle() {
- waitForIdleLooper(mObserverHandlerThread.getLooper(), WAIT_TIMEOUT_MS);
- waitForIdleLooper(mHandler.getLooper(), WAIT_TIMEOUT_MS);
+ waitForIdleHandler(mObserverHandlerThread, WAIT_TIMEOUT_MS);
+ waitForIdleHandler(mHandler, WAIT_TIMEOUT_MS);
}
-
- // TODO: unify with ConnectivityService.waitForIdleHandler and
- // NetworkServiceStatsTest.IdleableHandlerThread
- private static void waitForIdleLooper(Looper looper, long timeoutMs) {
- final ConditionVariable cv = new ConditionVariable();
- final Handler handler = new Handler(looper);
- handler.post(() -> cv.open());
- if (!cv.block(timeoutMs)) {
- fail("Looper did not become idle after " + timeoutMs + " ms");
- }
- }
-
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 029693f..814a626 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -31,6 +31,8 @@
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.SET_FOREGROUND;
+import static android.net.NetworkStats.STATS_PER_IFACE;
+import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsHistory.FIELD_ALL;
@@ -44,18 +46,17 @@
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
+import static com.android.internal.util.TestUtils.waitForIdleHandler;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Mockito.when;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.AlarmManager;
import android.app.usage.NetworkStatsManager;
@@ -76,24 +77,21 @@
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.INetworkManagementService;
import android.os.IBinder;
+import android.os.INetworkManagementService;
import android.os.Looper;
-import android.os.Messenger;
-import android.os.MessageQueue;
-import android.os.MessageQueue.IdleHandler;
import android.os.Message;
+import android.os.Messenger;
import android.os.PowerManager;
import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.telephony.TelephonyManager;
-import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
import android.util.TrustedTime;
import com.android.internal.net.VpnInfo;
import com.android.internal.util.test.BroadcastInterceptingContext;
-import com.android.server.net.NetworkStatsService;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
@@ -109,9 +107,7 @@
import org.mockito.MockitoAnnotations;
import java.io.File;
-import java.util.ArrayList;
import java.util.Objects;
-import java.util.List;
/**
* Tests for {@link NetworkStatsService}.
@@ -154,7 +150,7 @@
private @Mock IConnectivityManager mConnManager;
private @Mock IBinder mBinder;
private @Mock AlarmManager mAlarmManager;
- private IdleableHandlerThread mHandlerThread;
+ private HandlerThread mHandlerThread;
private Handler mHandler;
private NetworkStatsService mService;
@@ -181,7 +177,7 @@
mServiceContext, mNetManager, mAlarmManager, wakeLock, mTime,
TelephonyManager.getDefault(), mSettings, new NetworkStatsObservers(),
mStatsDir, getBaseDir(mStatsDir));
- mHandlerThread = new IdleableHandlerThread("HandlerThread");
+ mHandlerThread = new HandlerThread("HandlerThread");
mHandlerThread.start();
Handler.Callback callback = new NetworkStatsService.HandlerCallback(mService);
mHandler = new Handler(mHandlerThread.getLooper(), callback);
@@ -822,17 +818,24 @@
incrementCurrentTime(HOUR_IN_MILLIS);
expectCurrentTime();
expectDefaultSettings();
- expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
+ // Traffic seen by kernel counters (includes software tethering).
+ final NetworkStats ifaceStats = new NetworkStats(getElapsedRealtime(), 1)
+ .addIfaceValues(TEST_IFACE, 1536L, 12L, 384L, 3L);
+ // Hardware tethering traffic, not seen by kernel counters.
+ final NetworkStats tetherStatsHardware = new NetworkStats(getElapsedRealtime(), 1)
+ .addIfaceValues(TEST_IFACE, 512L, 4L, 128L, 1L);
+
+ // Traffic for UID_RED.
final NetworkStats uidStats = new NetworkStats(getElapsedRealtime(), 1)
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
- final String[] tetherIfacePairs = new String[] { TEST_IFACE, "wlan0" };
+ // All tethering traffic, both hardware and software.
final NetworkStats tetherStats = new NetworkStats(getElapsedRealtime(), 1)
.addValues(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
0L);
- expectNetworkStatsUidDetail(uidStats, tetherIfacePairs, tetherStats);
+ expectNetworkStatsSummary(ifaceStats, tetherStatsHardware);
+ expectNetworkStatsUidDetail(uidStats, tetherStats);
forcePollAndWaitForIdle();
// verify service recorded history
@@ -886,7 +889,7 @@
// Send dummy message to make sure that any previous message has been handled
mHandler.sendMessage(mHandler.obtainMessage(-1));
- mHandlerThread.waitForIdle(WAIT_TIMEOUT);
+ waitForIdleHandler(mHandler, WAIT_TIMEOUT);
@@ -908,7 +911,7 @@
assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0);
// make sure callback has not being called
- assertEquals(INVALID_TYPE, latchedHandler.mLastMessageType);
+ assertEquals(INVALID_TYPE, latchedHandler.lastMessageType);
// and bump forward again, with counters going higher. this is
// important, since it will trigger the data usage callback
@@ -926,7 +929,7 @@
// Wait for the caller to ack receipt of CALLBACK_LIMIT_REACHED
assertTrue(cv.block(WAIT_TIMEOUT));
- assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, latchedHandler.mLastMessageType);
+ assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, latchedHandler.lastMessageType);
cv.close();
// Allow binder to disconnect
@@ -937,7 +940,7 @@
// Wait for the caller to ack receipt of CALLBACK_RELEASED
assertTrue(cv.block(WAIT_TIMEOUT));
- assertEquals(NetworkStatsManager.CALLBACK_RELEASED, latchedHandler.mLastMessageType);
+ assertEquals(NetworkStatsManager.CALLBACK_RELEASED, latchedHandler.lastMessageType);
// Make sure that the caller binder gets disconnected
verify(mBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt());
@@ -973,7 +976,7 @@
// verify summary API
final NetworkStats stats = mSession.getSummaryForNetwork(template, start, end);
- assertValues(stats, IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
+ assertValues(stats, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
rxBytes, rxPackets, txBytes, txPackets, operations);
}
@@ -1012,10 +1015,16 @@
}
private void expectNetworkStatsSummary(NetworkStats summary) throws Exception {
+ expectNetworkStatsSummary(summary, new NetworkStats(0L, 0));
+ }
+
+ private void expectNetworkStatsSummary(NetworkStats summary, NetworkStats tetherStats)
+ throws Exception {
when(mConnManager.getAllVpnInfo()).thenReturn(new VpnInfo[0]);
- expectNetworkStatsSummaryDev(summary);
- expectNetworkStatsSummaryXt(summary);
+ expectNetworkStatsTethering(STATS_PER_IFACE, tetherStats);
+ expectNetworkStatsSummaryDev(summary.clone());
+ expectNetworkStatsSummaryXt(summary.clone());
}
private void expectNetworkStatsSummaryDev(NetworkStats summary) throws Exception {
@@ -1026,17 +1035,21 @@
when(mNetManager.getNetworkStatsSummaryXt()).thenReturn(summary);
}
- private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception {
- expectNetworkStatsUidDetail(detail, new String[0], new NetworkStats(0L, 0));
+ private void expectNetworkStatsTethering(int how, NetworkStats stats)
+ throws Exception {
+ when(mNetManager.getNetworkStatsTethering(how)).thenReturn(stats);
}
- private void expectNetworkStatsUidDetail(
- NetworkStats detail, String[] tetherIfacePairs, NetworkStats tetherStats)
+ private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception {
+ expectNetworkStatsUidDetail(detail, new NetworkStats(0L, 0));
+ }
+
+ private void expectNetworkStatsUidDetail(NetworkStats detail, NetworkStats tetherStats)
throws Exception {
when(mNetManager.getNetworkStatsUidDetail(UID_ALL)).thenReturn(detail);
// also include tethering details, since they are folded into UID
- when(mNetManager.getNetworkStatsTethering()).thenReturn(tetherStats);
+ when(mNetManager.getNetworkStatsTethering(STATS_PER_UID)).thenReturn(tetherStats);
}
private void expectDefaultSettings() throws Exception {
@@ -1087,28 +1100,25 @@
int tag, int metered, int roaming, long rxBytes, long rxPackets, long txBytes,
long txPackets, int operations) {
final NetworkStats.Entry entry = new NetworkStats.Entry();
- List<Integer> sets = new ArrayList<>();
- if (set == SET_DEFAULT || set == SET_ALL) {
- sets.add(SET_DEFAULT);
- }
- if (set == SET_FOREGROUND || set == SET_ALL) {
- sets.add(SET_FOREGROUND);
+ final int[] sets;
+ if (set == SET_ALL) {
+ sets = new int[] { SET_ALL, SET_DEFAULT, SET_FOREGROUND };
+ } else {
+ sets = new int[] { set };
}
- List<Integer> roamings = new ArrayList<>();
- if (roaming == ROAMING_NO || roaming == ROAMING_ALL) {
- roamings.add(ROAMING_NO);
- }
- if (roaming == ROAMING_YES || roaming == ROAMING_ALL) {
- roamings.add(ROAMING_YES);
+ final int[] roamings;
+ if (roaming == ROAMING_ALL) {
+ roamings = new int[] { ROAMING_ALL, ROAMING_YES, ROAMING_NO };
+ } else {
+ roamings = new int[] { roaming };
}
- List<Integer> meterings = new ArrayList<>();
- if (metered == METERED_NO || metered == METERED_ALL) {
- meterings.add(METERED_NO);
- }
- if (metered == METERED_YES || metered == METERED_ALL) {
- meterings.add(METERED_YES);
+ final int[] meterings;
+ if (metered == METERED_ALL) {
+ meterings = new int[] { METERED_ALL, METERED_YES, METERED_NO };
+ } else {
+ meterings = new int[] { metered };
}
for (int s : sets) {
@@ -1203,12 +1213,12 @@
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
// Send dummy message to make sure that any previous message has been handled
mHandler.sendMessage(mHandler.obtainMessage(-1));
- mHandlerThread.waitForIdle(WAIT_TIMEOUT);
+ waitForIdleHandler(mHandler, WAIT_TIMEOUT);
}
static class LatchedHandler extends Handler {
private final ConditionVariable mCv;
- int mLastMessageType = INVALID_TYPE;
+ int lastMessageType = INVALID_TYPE;
LatchedHandler(Looper looper, ConditionVariable cv) {
super(looper);
@@ -1217,49 +1227,9 @@
@Override
public void handleMessage(Message msg) {
- mLastMessageType = msg.what;
+ lastMessageType = msg.what;
mCv.open();
super.handleMessage(msg);
}
}
-
- /**
- * A subclass of HandlerThread that allows callers to wait for it to become idle. waitForIdle
- * will return immediately if the handler is already idle.
- */
- static class IdleableHandlerThread extends HandlerThread {
- private IdleHandler mIdleHandler;
-
- public IdleableHandlerThread(String name) {
- super(name);
- }
-
- public void waitForIdle(long timeoutMs) {
- final ConditionVariable cv = new ConditionVariable();
- final MessageQueue queue = getLooper().getQueue();
-
- synchronized (queue) {
- if (queue.isIdle()) {
- return;
- }
-
- assertNull("BUG: only one idle handler allowed", mIdleHandler);
- mIdleHandler = new IdleHandler() {
- public boolean queueIdle() {
- cv.open();
- mIdleHandler = null;
- return false; // Remove the handler.
- }
- };
- queue.addIdleHandler(mIdleHandler);
- }
-
- if (!cv.block(timeoutMs)) {
- fail("HandlerThread " + getName() + " did not become idle after " + timeoutMs
- + " ms");
- queue.removeIdleHandler(mIdleHandler);
- }
- }
- }
-
}