From ccd5ba288a8a9c4a4547079a20ec4f17031e55d7 Mon Sep 17 00:00:00 2001 From: Aaron Huang Date: Mon, 30 Mar 2020 06:38:49 +0000 Subject: [PATCH 1/3] Make LinkPropertiesTest compatible with Q CompareResult had been moved from LinkProperties to LinkPropertiesUtils so this change ignores testCompareResult() on Q and only check compareAllRoutes() works while android version is at least R. Bug: 151782584 Test: atest CtsNetTestCasesLatestSdk:LinkPropertiesTest Merged-In: I38b0d83abf983b3bcc01fc6aea2e5cc307734198 Change-Id: I38b0d83abf983b3bcc01fc6aea2e5cc307734198 --- .../java/android/net/LinkPropertiesTest.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java index 2b5720a47e..f163512ed4 100644 --- a/tests/net/common/java/android/net/LinkPropertiesTest.java +++ b/tests/net/common/java/android/net/LinkPropertiesTest.java @@ -445,14 +445,20 @@ public class LinkPropertiesTest { // Check comparisons work. LinkProperties lp2 = new LinkProperties(lp); assertAllRoutesHaveInterface("wlan0", lp2); - assertEquals(0, lp.compareAllRoutes(lp2).added.size()); - assertEquals(0, lp.compareAllRoutes(lp2).removed.size()); + // LinkProperties#compareAllRoutes exists both in R and before R, but the return type + // changed in R, so a test compiled with the R version of LinkProperties cannot run on Q. + if (isAtLeastR()) { + assertEquals(0, lp.compareAllRoutes(lp2).added.size()); + assertEquals(0, lp.compareAllRoutes(lp2).removed.size()); + } lp2.setInterfaceName("p2p0"); assertAllRoutesHaveInterface("p2p0", lp2); assertAllRoutesNotHaveInterface("wlan0", lp2); - assertEquals(3, lp.compareAllRoutes(lp2).added.size()); - assertEquals(3, lp.compareAllRoutes(lp2).removed.size()); + if (isAtLeastR()) { + assertEquals(3, lp.compareAllRoutes(lp2).added.size()); + assertEquals(3, lp.compareAllRoutes(lp2).removed.size()); + } // Remove route with incorrect interface, no route removed. lp.removeRoute(new RouteInfo(prefix2, null, null)); @@ -936,7 +942,7 @@ public class LinkPropertiesTest { } - @Test + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) public void testCompareResult() { // Either adding or removing items compareResult(Arrays.asList(1, 2, 3, 4), Arrays.asList(1), From 7fb7c3e0bf81bc76af84f299973c5a8d6e403228 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 2 Apr 2020 04:50:41 +0000 Subject: [PATCH 2/3] Only apply VPN isolation if it's fully routed VPN is considered fully routed if both IPv4 and IPv6 have either a default route or a prohibit route. Bug: 145332510 Test: atest FrameworksNetTests Merged-In: I59cf48552bca98092d1212e3d718fd420add5458 Change-Id: I59cf48552bca98092d1212e3d718fd420add5458 --- core/java/android/net/LinkProperties.java | 30 ++++++++++ core/java/android/net/RouteInfo.java | 26 +++++++++ .../android/server/ConnectivityService.java | 3 +- .../java/android/net/LinkPropertiesTest.java | 25 ++++++++ .../java/android/net/RouteInfoTest.java | 58 +++++++++++++++++++ .../server/ConnectivityServiceTest.java | 5 ++ 6 files changed, 146 insertions(+), 1 deletion(-) diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 7ff954bdc1..651494d1c9 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -1073,6 +1073,21 @@ public final class LinkProperties implements Parcelable { return false; } + /** + * Returns true if this link has an IPv4 unreachable default route. + * + * @return {@code true} if there is an IPv4 unreachable default route, {@code false} otherwise. + * @hide + */ + public boolean hasIpv4UnreachableDefaultRoute() { + for (RouteInfo r : mRoutes) { + if (r.isIPv4UnreachableDefault()) { + return true; + } + } + return false; + } + /** * For backward compatibility. * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely @@ -1101,6 +1116,21 @@ public final class LinkProperties implements Parcelable { return false; } + /** + * Returns true if this link has an IPv6 unreachable default route. + * + * @return {@code true} if there is an IPv6 unreachable default route, {@code false} otherwise. + * @hide + */ + public boolean hasIpv6UnreachableDefaultRoute() { + for (RouteInfo r : mRoutes) { + if (r.isIPv6UnreachableDefault()) { + return true; + } + } + return false; + } + /** * For backward compatibility. * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index dbdaa4c2da..e550f85e6b 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -425,6 +425,16 @@ public final class RouteInfo implements Parcelable { return mType == RTN_UNICAST && mDestination.getPrefixLength() == 0; } + /** + * Indicates if this route is an unreachable default route. + * + * @return {@code true} if it's an unreachable route with prefix length of 0. + * @hide + */ + private boolean isUnreachableDefaultRoute() { + return mType == RTN_UNREACHABLE && mDestination.getPrefixLength() == 0; + } + /** * Indicates if this route is an IPv4 default route. * @hide @@ -433,6 +443,14 @@ public final class RouteInfo implements Parcelable { return isDefaultRoute() && mDestination.getAddress() instanceof Inet4Address; } + /** + * Indicates if this route is an IPv4 unreachable default route. + * @hide + */ + public boolean isIPv4UnreachableDefault() { + return isUnreachableDefaultRoute() && mDestination.getAddress() instanceof Inet4Address; + } + /** * Indicates if this route is an IPv6 default route. * @hide @@ -441,6 +459,14 @@ public final class RouteInfo implements Parcelable { return isDefaultRoute() && mDestination.getAddress() instanceof Inet6Address; } + /** + * Indicates if this route is an IPv6 unreachable default route. + * @hide + */ + public boolean isIPv6UnreachableDefault() { + return isUnreachableDefaultRoute() && mDestination.getAddress() instanceof Inet6Address; + } + /** * Indicates if this route is a host route (ie, matches only a single host address). * diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 4ab035e7f4..2eaa766ad3 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -6313,7 +6313,8 @@ public class ConnectivityService extends IConnectivityManager.Stub && !nai.networkAgentConfig.allowBypass && nc.getOwnerUid() != Process.SYSTEM_UID && lp.getInterfaceName() != null - && (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute()); + && (lp.hasIPv4DefaultRoute() || lp.hasIpv4UnreachableDefaultRoute()) + && (lp.hasIPv6DefaultRoute() || lp.hasIpv6UnreachableDefaultRoute()); } private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc, diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java index b4f0daa025..939f8eb8c1 100644 --- a/tests/net/common/java/android/net/LinkPropertiesTest.java +++ b/tests/net/common/java/android/net/LinkPropertiesTest.java @@ -16,6 +16,8 @@ package android.net; +import static android.net.RouteInfo.RTN_UNREACHABLE; + import static com.android.testutils.ParcelUtilsKt.assertParcelSane; import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; import static com.android.testutils.ParcelUtilsKt.parcelingRoundTrip; @@ -46,6 +48,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; @@ -1251,4 +1254,26 @@ public class LinkPropertiesTest { final LinkProperties Ipv6 = makeIpv6LinkProperties(); assertTrue(Ipv6.hasIpv6DnsServer()); } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testHasIpv4UnreachableDefaultRoute() { + final LinkProperties lp = makeTestObject(); + assertFalse(lp.hasIpv4UnreachableDefaultRoute()); + assertFalse(lp.hasIpv6UnreachableDefaultRoute()); + + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); + assertTrue(lp.hasIpv4UnreachableDefaultRoute()); + assertFalse(lp.hasIpv6UnreachableDefaultRoute()); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testHasIpv6UnreachableDefaultRoute() { + final LinkProperties lp = makeTestObject(); + assertFalse(lp.hasIpv6UnreachableDefaultRoute()); + assertFalse(lp.hasIpv4UnreachableDefaultRoute()); + + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); + assertTrue(lp.hasIpv6UnreachableDefaultRoute()); + assertFalse(lp.hasIpv4UnreachableDefaultRoute()); + } } diff --git a/tests/net/common/java/android/net/RouteInfoTest.java b/tests/net/common/java/android/net/RouteInfoTest.java index 1658262c17..8204b494bb 100644 --- a/tests/net/common/java/android/net/RouteInfoTest.java +++ b/tests/net/common/java/android/net/RouteInfoTest.java @@ -31,6 +31,7 @@ import static org.junit.Assert.fail; import android.os.Build; +import androidx.core.os.BuildCompat; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -62,6 +63,11 @@ public class RouteInfoTest { return new IpPrefix(prefix); } + private static boolean isAtLeastR() { + // BuildCompat.isAtLeastR is documented to return false on release SDKs (including R) + return Build.VERSION.SDK_INT > Build.VERSION_CODES.Q || BuildCompat.isAtLeastR(); + } + @Test public void testConstructor() { RouteInfo r; @@ -195,78 +201,130 @@ public class RouteInfoTest { assertTrue(r.isDefaultRoute()); assertTrue(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("::/0"), Address("::"), "wlan0"); assertFalse(r.isHostRoute()); assertTrue(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertTrue(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0"); assertFalse(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("2001:db8::/48"), null, "wlan0"); assertFalse(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("192.0.2.0/32"), Address("0.0.0.0"), "wlan0"); assertTrue(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("2001:db8::/128"), Address("::"), "wlan0"); assertTrue(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("192.0.2.0/32"), null, "wlan0"); assertTrue(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("2001:db8::/128"), null, "wlan0"); assertTrue(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("::/128"), Address("fe80::"), "wlan0"); assertTrue(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("0.0.0.0/32"), Address("192.0.2.1"), "wlan0"); assertTrue(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("0.0.0.0/32"), Address("192.0.2.1"), "wlan0"); assertTrue(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE); assertFalse(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertTrue(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE); assertFalse(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertTrue(r.isIPv6UnreachableDefault()); + } } @Test diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index b02398d1f1..912a27f08f 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -6336,6 +6336,7 @@ public class ConnectivityServiceTest { LinkProperties lp = new LinkProperties(); lp.setInterfaceName("tun0"); lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); // The uid range needs to cover the test app so the network is visible to it. final Set vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange); @@ -6361,6 +6362,7 @@ public class ConnectivityServiceTest { public void testLegacyVpnDoesNotResultInInterfaceFilteringRule() throws Exception { LinkProperties lp = new LinkProperties(); lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final Set vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); @@ -6392,6 +6394,7 @@ public class ConnectivityServiceTest { LinkProperties lp = new LinkProperties(); lp.setInterfaceName("tun0"); lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final Set vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange); @@ -6428,6 +6431,7 @@ public class ConnectivityServiceTest { reset(mMockNetd); lp = new LinkProperties(); lp.setInterfaceName("tun1"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); vpnNetworkAgent.sendLinkProperties(lp); waitForIdle(); @@ -6440,6 +6444,7 @@ public class ConnectivityServiceTest { public void testUidUpdateChangesInterfaceFilteringRule() throws Exception { LinkProperties lp = new LinkProperties(); lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final UidRange vpnRange = UidRange.createForUser(VPN_USER); From 71863e96047070ac88d152b692ea62da3a609cbb Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Thu, 2 Apr 2020 15:34:33 -0700 Subject: [PATCH 3/3] Added a new capability for temporarily unmetered network. Added a new network capability TEMOPORARILY_NOT_METERED to support the case that a network can temporarily become unmetered. This allows carriers to deploy unmetered 5G network. When devices camp on 5G network, this capability will be dynamically added to the network and will be removed once leaving 5G coverage. Bug: 153081494 Test: Manual Change-Id: I10e26cb0852e67f614e7b9c4e49f95e078602e21 --- core/java/android/net/NetworkCapabilities.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 9ff7ebee6d..73c6b3daf2 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -169,6 +169,7 @@ public final class NetworkCapabilities implements Parcelable { NET_CAPABILITY_OEM_PAID, NET_CAPABILITY_MCX, NET_CAPABILITY_PARTIAL_CONNECTIVITY, + NET_CAPABILITY_TEMPORARILY_NOT_METERED, }) public @interface NetCapability { } @@ -336,8 +337,16 @@ public final class NetworkCapabilities implements Parcelable { @SystemApi public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; + /** + * This capability will be set for networks that are generally metered, but are currently + * unmetered, e.g., because the user is in a particular area. This capability can be changed at + * any time. When it is removed, applications are responsible for stopping any data transfer + * that should not occur on a metered network. + */ + public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25; + private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS; - private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_PARTIAL_CONNECTIVITY; + private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_TEMPORARILY_NOT_METERED; /** * Network capabilities that are expected to be mutable, i.e., can change while a particular @@ -353,7 +362,8 @@ public final class NetworkCapabilities implements Parcelable { | (1 << NET_CAPABILITY_FOREGROUND) | (1 << NET_CAPABILITY_NOT_CONGESTED) | (1 << NET_CAPABILITY_NOT_SUSPENDED) - | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY); + | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY + | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)); /** * Network capabilities that are not allowed in NetworkRequests. This exists because the @@ -424,6 +434,7 @@ public final class NetworkCapabilities implements Parcelable { */ private static final long TEST_NETWORKS_ALLOWED_CAPABILITIES = (1 << NET_CAPABILITY_NOT_METERED) + | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED) | (1 << NET_CAPABILITY_NOT_RESTRICTED) | (1 << NET_CAPABILITY_NOT_VPN) | (1 << NET_CAPABILITY_NOT_ROAMING) @@ -1864,6 +1875,7 @@ public final class NetworkCapabilities implements Parcelable { case NET_CAPABILITY_OEM_PAID: return "OEM_PAID"; case NET_CAPABILITY_MCX: return "MCX"; case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY"; + case NET_CAPABILITY_TEMPORARILY_NOT_METERED: return "TEMPORARILY_NOT_METERED"; default: return Integer.toString(capability); } }