diff options
| -rw-r--r-- | core/java/android/net/LinkProperties.java | 70 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/net/LinkPropertiesTest.java | 77 |
2 files changed, 140 insertions, 7 deletions
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 8b0dfc95fef0..31aedad77116 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -88,6 +88,54 @@ public final class LinkProperties implements Parcelable { /** * @hide */ + public enum ProvisioningChange { + STILL_NOT_PROVISIONED, + LOST_PROVISIONING, + GAINED_PROVISIONING, + STILL_PROVISIONED, + } + + /** + * Compare the provisioning states of two LinkProperties instances. + * + * @hide + */ + public static ProvisioningChange compareProvisioning( + LinkProperties before, LinkProperties after) { + if (before.isProvisioned() && after.isProvisioned()) { + // On dualstack networks, DHCPv4 renewals can occasionally fail. + // When this happens, IPv6-reachable services continue to function + // normally but IPv4-only services (naturally) fail. + // + // When an application using an IPv4-only service reports a bad + // network condition to the framework, attempts to re-validate + // the network succeed (since we support IPv6-only networks) and + // nothing is changed. + // + // For users, this is confusing and unexpected behaviour, and is + // not necessarily easy to diagnose. Therefore, we treat changing + // from a dualstack network to an IPv6-only network equivalent to + // a total loss of provisioning. + // + // For one such example of this, see b/18867306. + // + // TODO: Remove this special case altogether. + if (before.isIPv4Provisioned() && !after.isIPv4Provisioned()) { + return ProvisioningChange.LOST_PROVISIONING; + } + return ProvisioningChange.STILL_PROVISIONED; + } else if (before.isProvisioned() && !after.isProvisioned()) { + return ProvisioningChange.LOST_PROVISIONING; + } else if (!before.isProvisioned() && after.isProvisioned()) { + return ProvisioningChange.GAINED_PROVISIONING; + } else { // !before.isProvisioned() && !after.isProvisioned() + return ProvisioningChange.STILL_NOT_PROVISIONED; + } + } + + /** + * @hide + */ public LinkProperties() { } @@ -287,6 +335,20 @@ public final class LinkProperties implements Parcelable { } /** + * Removes the given {@link InetAddress} from the list of DNS servers. + * + * @param dnsServer The {@link InetAddress} to remove from the list of DNS servers. + * @return true if the DNS server was removed, false if it did not exist. + * @hide + */ + public boolean removeDnsServer(InetAddress dnsServer) { + if (dnsServer != null) { + return mDnses.remove(dnsServer); + } + return false; + } + + /** * Replaces the DNS servers in this {@code LinkProperties} with * the given {@link Collection} of {@link InetAddress} objects. * @@ -679,8 +741,9 @@ public final class LinkProperties implements Parcelable { * This requires an IP address, default route, and DNS server. * * @return {@code true} if the link is provisioned, {@code false} otherwise. + * @hide */ - private boolean hasIPv4() { + public boolean isIPv4Provisioned() { return (hasIPv4Address() && hasIPv4DefaultRoute() && hasIPv4DnsServer()); @@ -691,8 +754,9 @@ public final class LinkProperties implements Parcelable { * This requires an IP address, default route, and DNS server. * * @return {@code true} if the link is provisioned, {@code false} otherwise. + * @hide */ - private boolean hasIPv6() { + public boolean isIPv6Provisioned() { return (hasGlobalIPv6Address() && hasIPv6DefaultRoute() && hasIPv6DnsServer()); @@ -706,7 +770,7 @@ public final class LinkProperties implements Parcelable { * @hide */ public boolean isProvisioned() { - return (hasIPv4() || hasIPv6()); + return (isIPv4Provisioned() || isIPv6Provisioned()); } /** diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java index abfed6e6f89a..5c55efb837fa 100644 --- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java +++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java @@ -17,6 +17,7 @@ package android.net; import android.net.LinkProperties; +import android.net.LinkProperties.ProvisioningChange; import android.net.RouteInfo; import android.system.OsConstants; import android.test.suitebuilder.annotation.SmallTest; @@ -34,7 +35,8 @@ public class LinkPropertiesTest extends TestCase { private static InetAddress DNS6 = NetworkUtils.numericToInetAddress("2001:4860:4860::8888"); private static InetAddress GATEWAY1 = NetworkUtils.numericToInetAddress("75.208.8.1"); private static InetAddress GATEWAY2 = NetworkUtils.numericToInetAddress("69.78.8.1"); - private static InetAddress GATEWAY6 = NetworkUtils.numericToInetAddress("fe80::6:0000:613"); + private static InetAddress GATEWAY61 = NetworkUtils.numericToInetAddress("fe80::6:0000:613"); + private static InetAddress GATEWAY62 = NetworkUtils.numericToInetAddress("fe80::6:2222"); private static String NAME = "qmi0"; private static int MTU = 1500; @@ -466,6 +468,8 @@ public class LinkPropertiesTest extends TestCase { assertFalse("v4only:addr+dns", lp4.isProvisioned()); lp4.addRoute(new RouteInfo(GATEWAY1)); assertTrue("v4only:addr+dns+route", lp4.isProvisioned()); + assertTrue("v4only:addr+dns+route", lp4.isIPv4Provisioned()); + assertFalse("v4only:addr+dns+route", lp4.isIPv6Provisioned()); LinkProperties lp6 = new LinkProperties(); assertFalse("v6only:empty", lp6.isProvisioned()); @@ -473,11 +477,14 @@ public class LinkPropertiesTest extends TestCase { assertFalse("v6only:fe80-only", lp6.isProvisioned()); lp6.addDnsServer(DNS6); assertFalse("v6only:fe80+dns", lp6.isProvisioned()); - lp6.addRoute(new RouteInfo(GATEWAY6)); + lp6.addRoute(new RouteInfo(GATEWAY61)); assertFalse("v6only:fe80+dns+route", lp6.isProvisioned()); lp6.addLinkAddress(LINKADDRV6); + assertTrue("v6only:fe80+global+dns+route", lp6.isIPv6Provisioned()); assertTrue("v6only:fe80+global+dns+route", lp6.isProvisioned()); lp6.removeLinkAddress(LINKADDRV6LINKLOCAL); + assertFalse("v6only:global+dns+route", lp6.isIPv4Provisioned()); + assertTrue("v6only:global+dns+route", lp6.isIPv6Provisioned()); assertTrue("v6only:global+dns+route", lp6.isProvisioned()); LinkProperties lp46 = new LinkProperties(); @@ -487,15 +494,77 @@ public class LinkPropertiesTest extends TestCase { lp46.addDnsServer(DNS6); assertFalse("dualstack:missing-routes", lp46.isProvisioned()); lp46.addRoute(new RouteInfo(GATEWAY1)); + assertTrue("dualstack:v4-provisioned", lp46.isIPv4Provisioned()); + assertFalse("dualstack:v4-provisioned", lp46.isIPv6Provisioned()); assertTrue("dualstack:v4-provisioned", lp46.isProvisioned()); - lp6.addRoute(new RouteInfo(GATEWAY6)); + lp46.addRoute(new RouteInfo(GATEWAY61)); + assertTrue("dualstack:both-provisioned", lp46.isIPv4Provisioned()); + assertTrue("dualstack:both-provisioned", lp46.isIPv6Provisioned()); assertTrue("dualstack:both-provisioned", lp46.isProvisioned()); // A link with an IPv6 address and default route, but IPv4 DNS server. LinkProperties mixed = new LinkProperties(); mixed.addLinkAddress(LINKADDRV6); mixed.addDnsServer(DNS1); - mixed.addRoute(new RouteInfo(GATEWAY6)); + mixed.addRoute(new RouteInfo(GATEWAY61)); + assertFalse("mixed:addr6+route6+dns4", mixed.isIPv4Provisioned()); + assertFalse("mixed:addr6+route6+dns4", mixed.isIPv6Provisioned()); assertFalse("mixed:addr6+route6+dns4", mixed.isProvisioned()); } + + @SmallTest + public void testCompareProvisioning() { + LinkProperties v4lp = new LinkProperties(); + v4lp.addLinkAddress(LINKADDRV4); + v4lp.addRoute(new RouteInfo(GATEWAY1)); + v4lp.addDnsServer(DNS1); + assertTrue(v4lp.isProvisioned()); + + LinkProperties v4r = new LinkProperties(v4lp); + v4r.removeDnsServer(DNS1); + assertFalse(v4r.isProvisioned()); + + assertEquals(ProvisioningChange.STILL_NOT_PROVISIONED, + LinkProperties.compareProvisioning(v4r, v4r)); + assertEquals(ProvisioningChange.LOST_PROVISIONING, + LinkProperties.compareProvisioning(v4lp, v4r)); + assertEquals(ProvisioningChange.GAINED_PROVISIONING, + LinkProperties.compareProvisioning(v4r, v4lp)); + assertEquals(ProvisioningChange.STILL_PROVISIONED, + LinkProperties.compareProvisioning(v4lp, v4lp)); + + // Check that losing IPv4 provisioning on a dualstack network is + // seen as a total loss of provisioning. + LinkProperties v6lp = new LinkProperties(); + v6lp.addLinkAddress(LINKADDRV6); + v6lp.addRoute(new RouteInfo(GATEWAY61)); + v6lp.addDnsServer(DNS6); + assertFalse(v6lp.isIPv4Provisioned()); + assertTrue(v6lp.isIPv6Provisioned()); + assertTrue(v6lp.isProvisioned()); + + LinkProperties v46lp = new LinkProperties(v6lp); + v46lp.addLinkAddress(LINKADDRV4); + v46lp.addRoute(new RouteInfo(GATEWAY1)); + v46lp.addDnsServer(DNS1); + assertTrue(v46lp.isIPv4Provisioned()); + assertTrue(v46lp.isIPv6Provisioned()); + assertTrue(v46lp.isProvisioned()); + + assertEquals(ProvisioningChange.STILL_PROVISIONED, + LinkProperties.compareProvisioning(v6lp, v46lp)); + assertEquals(ProvisioningChange.LOST_PROVISIONING, + LinkProperties.compareProvisioning(v46lp, v6lp)); + + // Check that losing and gaining a secondary router does not change + // the provisioning status. + LinkProperties v6lp2 = new LinkProperties(v6lp); + v6lp2.addRoute(new RouteInfo(GATEWAY62)); + assertTrue(v6lp2.isProvisioned()); + + assertEquals(ProvisioningChange.STILL_PROVISIONED, + LinkProperties.compareProvisioning(v6lp2, v6lp)); + assertEquals(ProvisioningChange.STILL_PROVISIONED, + LinkProperties.compareProvisioning(v6lp, v6lp2)); + } } |