Loading core/java/android/net/LinkProperties.java +67 −3 Original line number Diff line number Diff line Loading @@ -85,6 +85,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 */ Loading Loading @@ -286,6 +334,20 @@ public final class LinkProperties implements Parcelable { return false; } /** * 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. Loading Loading @@ -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()); Loading @@ -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()); Loading @@ -706,7 +770,7 @@ public final class LinkProperties implements Parcelable { * @hide */ public boolean isProvisioned() { return (hasIPv4() || hasIPv6()); return (isIPv4Provisioned() || isIPv6Provisioned()); } /** Loading core/tests/coretests/src/android/net/LinkPropertiesTest.java +73 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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()); Loading @@ -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(); Loading @@ -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)); } } Loading
core/java/android/net/LinkProperties.java +67 −3 Original line number Diff line number Diff line Loading @@ -85,6 +85,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 */ Loading Loading @@ -286,6 +334,20 @@ public final class LinkProperties implements Parcelable { return false; } /** * 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. Loading Loading @@ -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()); Loading @@ -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()); Loading @@ -706,7 +770,7 @@ public final class LinkProperties implements Parcelable { * @hide */ public boolean isProvisioned() { return (hasIPv4() || hasIPv6()); return (isIPv4Provisioned() || isIPv6Provisioned()); } /** Loading
core/tests/coretests/src/android/net/LinkPropertiesTest.java +73 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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()); Loading @@ -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(); Loading @@ -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)); } }