LinkProperties function to compare provisioning and remove DNS servers

Adds:
    - enum ProvisioningChange
    - LinkProperties#compareProvisioning()
          return a ProvisioningChange value describing the delta in
          provisioning between two LinkProperties objects
    - LinkProperties#removeDnsServer()
    - make "@hide public" isIPv4Provisioned() and isIPv6Provisioned()

Bug: 18581716
Change-Id: I3df90b2b89617f693346f2dbe72e77c88ce91ffd
This commit is contained in:
Erik Kline
2015-05-21 16:15:02 +09:00
parent c7830c6dcf
commit 04612b0692
2 changed files with 140 additions and 7 deletions

View File

@@ -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 * @hide
*/ */
@@ -286,6 +334,20 @@ public final class LinkProperties implements Parcelable {
return false; 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 * Replaces the DNS servers in this {@code LinkProperties} with
* the given {@link Collection} of {@link InetAddress} objects. * 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. * This requires an IP address, default route, and DNS server.
* *
* @return {@code true} if the link is provisioned, {@code false} otherwise. * @return {@code true} if the link is provisioned, {@code false} otherwise.
* @hide
*/ */
private boolean hasIPv4() { public boolean isIPv4Provisioned() {
return (hasIPv4Address() && return (hasIPv4Address() &&
hasIPv4DefaultRoute() && hasIPv4DefaultRoute() &&
hasIPv4DnsServer()); hasIPv4DnsServer());
@@ -691,8 +754,9 @@ public final class LinkProperties implements Parcelable {
* This requires an IP address, default route, and DNS server. * This requires an IP address, default route, and DNS server.
* *
* @return {@code true} if the link is provisioned, {@code false} otherwise. * @return {@code true} if the link is provisioned, {@code false} otherwise.
* @hide
*/ */
private boolean hasIPv6() { public boolean isIPv6Provisioned() {
return (hasGlobalIPv6Address() && return (hasGlobalIPv6Address() &&
hasIPv6DefaultRoute() && hasIPv6DefaultRoute() &&
hasIPv6DnsServer()); hasIPv6DnsServer());
@@ -706,7 +770,7 @@ public final class LinkProperties implements Parcelable {
* @hide * @hide
*/ */
public boolean isProvisioned() { public boolean isProvisioned() {
return (hasIPv4() || hasIPv6()); return (isIPv4Provisioned() || isIPv6Provisioned());
} }
/** /**

View File

@@ -17,6 +17,7 @@
package android.net; package android.net;
import android.net.LinkProperties; import android.net.LinkProperties;
import android.net.LinkProperties.ProvisioningChange;
import android.net.RouteInfo; import android.net.RouteInfo;
import android.system.OsConstants; import android.system.OsConstants;
import android.test.suitebuilder.annotation.SmallTest; 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 DNS6 = NetworkUtils.numericToInetAddress("2001:4860:4860::8888");
private static InetAddress GATEWAY1 = NetworkUtils.numericToInetAddress("75.208.8.1"); private static InetAddress GATEWAY1 = NetworkUtils.numericToInetAddress("75.208.8.1");
private static InetAddress GATEWAY2 = NetworkUtils.numericToInetAddress("69.78.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 String NAME = "qmi0";
private static int MTU = 1500; private static int MTU = 1500;
@@ -466,6 +468,8 @@ public class LinkPropertiesTest extends TestCase {
assertFalse("v4only:addr+dns", lp4.isProvisioned()); assertFalse("v4only:addr+dns", lp4.isProvisioned());
lp4.addRoute(new RouteInfo(GATEWAY1)); lp4.addRoute(new RouteInfo(GATEWAY1));
assertTrue("v4only:addr+dns+route", lp4.isProvisioned()); 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(); LinkProperties lp6 = new LinkProperties();
assertFalse("v6only:empty", lp6.isProvisioned()); assertFalse("v6only:empty", lp6.isProvisioned());
@@ -473,11 +477,14 @@ public class LinkPropertiesTest extends TestCase {
assertFalse("v6only:fe80-only", lp6.isProvisioned()); assertFalse("v6only:fe80-only", lp6.isProvisioned());
lp6.addDnsServer(DNS6); lp6.addDnsServer(DNS6);
assertFalse("v6only:fe80+dns", lp6.isProvisioned()); assertFalse("v6only:fe80+dns", lp6.isProvisioned());
lp6.addRoute(new RouteInfo(GATEWAY6)); lp6.addRoute(new RouteInfo(GATEWAY61));
assertFalse("v6only:fe80+dns+route", lp6.isProvisioned()); assertFalse("v6only:fe80+dns+route", lp6.isProvisioned());
lp6.addLinkAddress(LINKADDRV6); lp6.addLinkAddress(LINKADDRV6);
assertTrue("v6only:fe80+global+dns+route", lp6.isIPv6Provisioned());
assertTrue("v6only:fe80+global+dns+route", lp6.isProvisioned()); assertTrue("v6only:fe80+global+dns+route", lp6.isProvisioned());
lp6.removeLinkAddress(LINKADDRV6LINKLOCAL); lp6.removeLinkAddress(LINKADDRV6LINKLOCAL);
assertFalse("v6only:global+dns+route", lp6.isIPv4Provisioned());
assertTrue("v6only:global+dns+route", lp6.isIPv6Provisioned());
assertTrue("v6only:global+dns+route", lp6.isProvisioned()); assertTrue("v6only:global+dns+route", lp6.isProvisioned());
LinkProperties lp46 = new LinkProperties(); LinkProperties lp46 = new LinkProperties();
@@ -487,15 +494,77 @@ public class LinkPropertiesTest extends TestCase {
lp46.addDnsServer(DNS6); lp46.addDnsServer(DNS6);
assertFalse("dualstack:missing-routes", lp46.isProvisioned()); assertFalse("dualstack:missing-routes", lp46.isProvisioned());
lp46.addRoute(new RouteInfo(GATEWAY1)); lp46.addRoute(new RouteInfo(GATEWAY1));
assertTrue("dualstack:v4-provisioned", lp46.isIPv4Provisioned());
assertFalse("dualstack:v4-provisioned", lp46.isIPv6Provisioned());
assertTrue("dualstack:v4-provisioned", lp46.isProvisioned()); 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()); assertTrue("dualstack:both-provisioned", lp46.isProvisioned());
// A link with an IPv6 address and default route, but IPv4 DNS server. // A link with an IPv6 address and default route, but IPv4 DNS server.
LinkProperties mixed = new LinkProperties(); LinkProperties mixed = new LinkProperties();
mixed.addLinkAddress(LINKADDRV6); mixed.addLinkAddress(LINKADDRV6);
mixed.addDnsServer(DNS1); 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()); 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));
}
} }