From 1e86b3af2633a31a13285e51d63f40cd88697a3c Mon Sep 17 00:00:00 2001 From: Etan Cohen Date: Sun, 12 Dec 2021 02:14:06 +0000 Subject: [PATCH] Expose public APIs for IP & static IP configuration Create public API for IP and static IP configuration. Bug: 209840828 Test: atest android.net.cts.IpConfigurationTest Test: atest android.net.cts.StaticIpConfigurationTest Test: atest android.net.dhcp.DhcpResultsParcelableUtilTest Change-Id: I720f168d1023806970919ca5dd44239a276826b6 --- framework/api/current.txt | 34 +++++++ framework/api/system-current.txt | 21 ----- .../src/android/net/IpConfiguration.java | 88 ++++++++++++++++++- .../android/net/StaticIpConfiguration.java | 55 +++++++----- .../android/net/cts/IpConfigurationTest.java | 19 ++++ .../net/cts}/StaticIpConfigurationTest.java | 52 +++++++++-- 6 files changed, 216 insertions(+), 53 deletions(-) rename tests/{common/java/android/net => cts/net/src/android/net/cts}/StaticIpConfigurationTest.java (81%) diff --git a/framework/api/current.txt b/framework/api/current.txt index a373b71e01..547b7e26a6 100644 --- a/framework/api/current.txt +++ b/framework/api/current.txt @@ -205,6 +205,21 @@ package android.net { method @NonNull public static java.net.InetAddress parseNumericAddress(@NonNull String); } + public final class IpConfiguration implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.net.ProxyInfo getHttpProxy(); + method @Nullable public android.net.StaticIpConfiguration getStaticIpConfiguration(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class IpConfiguration.Builder { + ctor public IpConfiguration.Builder(); + method @NonNull public android.net.IpConfiguration build(); + method @NonNull public android.net.IpConfiguration.Builder setHttpProxy(@Nullable android.net.ProxyInfo); + method @NonNull public android.net.IpConfiguration.Builder setStaticIpConfiguration(@Nullable android.net.StaticIpConfiguration); + } + public final class IpPrefix implements android.os.Parcelable { ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); method public boolean contains(@NonNull java.net.InetAddress); @@ -485,6 +500,25 @@ package android.net { method public void onStopped(); } + public final class StaticIpConfiguration implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List getDnsServers(); + method @Nullable public String getDomains(); + method @Nullable public java.net.InetAddress getGateway(); + method @NonNull public android.net.LinkAddress getIpAddress(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class StaticIpConfiguration.Builder { + ctor public StaticIpConfiguration.Builder(); + method @NonNull public android.net.StaticIpConfiguration build(); + method @NonNull public android.net.StaticIpConfiguration.Builder setDnsServers(@NonNull Iterable); + method @NonNull public android.net.StaticIpConfiguration.Builder setDomains(@Nullable String); + method @NonNull public android.net.StaticIpConfiguration.Builder setGateway(@Nullable java.net.InetAddress); + method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@NonNull android.net.LinkAddress); + } + public interface TransportInfo { } diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt index d420958430..764cffa92a 100644 --- a/framework/api/system-current.txt +++ b/framework/api/system-current.txt @@ -133,17 +133,12 @@ package android.net { public final class IpConfiguration implements android.os.Parcelable { ctor public IpConfiguration(); ctor public IpConfiguration(@NonNull android.net.IpConfiguration); - method public int describeContents(); - method @Nullable public android.net.ProxyInfo getHttpProxy(); method @NonNull public android.net.IpConfiguration.IpAssignment getIpAssignment(); method @NonNull public android.net.IpConfiguration.ProxySettings getProxySettings(); - method @Nullable public android.net.StaticIpConfiguration getStaticIpConfiguration(); method public void setHttpProxy(@Nullable android.net.ProxyInfo); method public void setIpAssignment(@NonNull android.net.IpConfiguration.IpAssignment); method public void setProxySettings(@NonNull android.net.IpConfiguration.ProxySettings); method public void setStaticIpConfiguration(@Nullable android.net.StaticIpConfiguration); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator CREATOR; } public enum IpConfiguration.IpAssignment { @@ -484,23 +479,7 @@ package android.net { ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration); method public void addDnsServer(@NonNull java.net.InetAddress); method public void clear(); - method public int describeContents(); - method @NonNull public java.util.List getDnsServers(); - method @Nullable public String getDomains(); - method @Nullable public java.net.InetAddress getGateway(); - method @Nullable public android.net.LinkAddress getIpAddress(); method @NonNull public java.util.List getRoutes(@Nullable String); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator CREATOR; - } - - public static final class StaticIpConfiguration.Builder { - ctor public StaticIpConfiguration.Builder(); - method @NonNull public android.net.StaticIpConfiguration build(); - method @NonNull public android.net.StaticIpConfiguration.Builder setDnsServers(@NonNull Iterable); - method @NonNull public android.net.StaticIpConfiguration.Builder setDomains(@Nullable String); - method @NonNull public android.net.StaticIpConfiguration.Builder setGateway(@Nullable java.net.InetAddress); - method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress); } public final class TcpKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable { diff --git a/framework/src/android/net/IpConfiguration.java b/framework/src/android/net/IpConfiguration.java index d5f8b2edb6..99835aa6fb 100644 --- a/framework/src/android/net/IpConfiguration.java +++ b/framework/src/android/net/IpConfiguration.java @@ -28,16 +28,16 @@ import android.os.Parcelable; import java.util.Objects; /** - * A class representing a configured network. - * @hide + * A class representing the IP configuration of a network. */ -@SystemApi public final class IpConfiguration implements Parcelable { private static final String TAG = "IpConfiguration"; // This enum has been used by apps through reflection for many releases. // Therefore they can't just be removed. Duplicating these constants to // give an alternate SystemApi is a worse option than exposing them. + /** @hide */ + @SystemApi @SuppressLint("Enum") public enum IpAssignment { /* Use statically configured IP settings. Configuration can be accessed @@ -59,6 +59,8 @@ public final class IpConfiguration implements Parcelable { // This enum has been used by apps through reflection for many releases. // Therefore they can't just be removed. Duplicating these constants to // give an alternate SystemApi is a worse option than exposing them. + /** @hide */ + @SystemApi @SuppressLint("Enum") public enum ProxySettings { /* No proxy is to be used. Any existing proxy settings @@ -94,6 +96,8 @@ public final class IpConfiguration implements Parcelable { null : new ProxyInfo(httpProxy); } + /** @hide */ + @SystemApi public IpConfiguration() { init(IpAssignment.UNASSIGNED, ProxySettings.UNASSIGNED, null, null); } @@ -107,6 +111,8 @@ public final class IpConfiguration implements Parcelable { init(ipAssignment, proxySettings, staticIpConfiguration, httpProxy); } + /** @hide */ + @SystemApi public IpConfiguration(@NonNull IpConfiguration source) { this(); if (source != null) { @@ -115,34 +121,58 @@ public final class IpConfiguration implements Parcelable { } } + /** @hide */ + @SystemApi public @NonNull IpAssignment getIpAssignment() { return ipAssignment; } + /** @hide */ + @SystemApi public void setIpAssignment(@NonNull IpAssignment ipAssignment) { this.ipAssignment = ipAssignment; } + /** + * Get the current static IP configuration (possibly null). Configured via + * {@link Builder#setStaticIpConfiguration(StaticIpConfiguration)}. + * + * @return Current static IP configuration. + */ public @Nullable StaticIpConfiguration getStaticIpConfiguration() { return staticIpConfiguration; } + /** @hide */ + @SystemApi public void setStaticIpConfiguration(@Nullable StaticIpConfiguration staticIpConfiguration) { this.staticIpConfiguration = staticIpConfiguration; } + /** @hide */ + @SystemApi public @NonNull ProxySettings getProxySettings() { return proxySettings; } + /** @hide */ + @SystemApi public void setProxySettings(@NonNull ProxySettings proxySettings) { this.proxySettings = proxySettings; } + /** + * The proxy configuration of this object. + * + * @return The proxy information of this object configured via + * {@link Builder#setHttpProxy(ProxyInfo)}. + */ public @Nullable ProxyInfo getHttpProxy() { return httpProxy; } + /** @hide */ + @SystemApi public void setHttpProxy(@Nullable ProxyInfo httpProxy) { this.httpProxy = httpProxy; } @@ -220,4 +250,56 @@ public final class IpConfiguration implements Parcelable { return new IpConfiguration[size]; } }; + + /** + * Builder used to construct {@link IpConfiguration} objects. + */ + public static final class Builder { + private StaticIpConfiguration mStaticIpConfiguration; + private ProxyInfo mProxyInfo; + + /** + * Set a static IP configuration. + * + * @param config Static IP configuration. + * @return A {@link Builder} object to allow chaining. + */ + public @NonNull Builder setStaticIpConfiguration(@Nullable StaticIpConfiguration config) { + mStaticIpConfiguration = config; + return this; + } + + /** + * Set a proxy configuration. + * + * @param proxyInfo Proxy configuration. + * @return A {@link Builder} object to allow chaining. + */ + public @NonNull Builder setHttpProxy(@Nullable ProxyInfo proxyInfo) { + mProxyInfo = proxyInfo; + return this; + } + + /** + * Construct an {@link IpConfiguration}. + * + * @return A new {@link IpConfiguration} object. + */ + public @NonNull IpConfiguration build() { + IpConfiguration config = new IpConfiguration(); + config.setStaticIpConfiguration(mStaticIpConfiguration); + config.setIpAssignment( + mStaticIpConfiguration == null ? IpAssignment.DHCP : IpAssignment.STATIC); + + config.setHttpProxy(mProxyInfo); + if (mProxyInfo == null) { + config.setProxySettings(ProxySettings.NONE); + } else { + config.setProxySettings( + mProxyInfo.getPacFileUrl() == null ? ProxySettings.STATIC + : ProxySettings.PAC); + } + return config; + } + } } diff --git a/framework/src/android/net/StaticIpConfiguration.java b/framework/src/android/net/StaticIpConfiguration.java index 7904f7a4ec..194cffd164 100644 --- a/framework/src/android/net/StaticIpConfiguration.java +++ b/framework/src/android/net/StaticIpConfiguration.java @@ -26,6 +26,7 @@ import android.os.Parcelable; import com.android.net.module.util.InetAddressUtils; +import java.net.Inet4Address; import java.net.InetAddress; import java.util.ArrayList; import java.util.List; @@ -33,24 +34,7 @@ import java.util.Objects; /** * Class that describes static IP configuration. - * - *

This class is different from {@link LinkProperties} because it represents - * configuration intent. The general contract is that if we can represent - * a configuration here, then we should be able to configure it on a network. - * The intent is that it closely match the UI we have for configuring networks. - * - *

In contrast, {@link LinkProperties} represents current state. It is much more - * expressive. For example, it supports multiple IP addresses, multiple routes, - * stacked interfaces, and so on. Because LinkProperties is so expressive, - * using it to represent configuration intent as well as current state causes - * problems. For example, we could unknowingly save a configuration that we are - * not in fact capable of applying, or we could save a configuration that the - * UI cannot display, which has the potential for malicious code to hide - * hostile or unexpected configuration from the user. - * - * @hide */ -@SystemApi public final class StaticIpConfiguration implements Parcelable { /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -69,10 +53,14 @@ public final class StaticIpConfiguration implements Parcelable { @Nullable public String domains; + /** @hide */ + @SystemApi public StaticIpConfiguration() { dnsServers = new ArrayList<>(); } + /** @hide */ + @SystemApi public StaticIpConfiguration(@Nullable StaticIpConfiguration source) { this(); if (source != null) { @@ -84,6 +72,8 @@ public final class StaticIpConfiguration implements Parcelable { } } + /** @hide */ + @SystemApi public void clear() { ipAddress = null; gateway = null; @@ -94,7 +84,7 @@ public final class StaticIpConfiguration implements Parcelable { /** * Get the static IP address included in the configuration. */ - public @Nullable LinkAddress getIpAddress() { + public @NonNull LinkAddress getIpAddress() { return ipAddress; } @@ -130,10 +120,15 @@ public final class StaticIpConfiguration implements Parcelable { private String mDomains; /** - * Set the IP address to be included in the configuration; null by default. + * Set the IP address to be included in the configuration. + * * @return The {@link Builder} for chaining. */ - public @NonNull Builder setIpAddress(@Nullable LinkAddress ipAddress) { + public @NonNull Builder setIpAddress(@NonNull LinkAddress ipAddress) { + if (ipAddress != null && !(ipAddress.getAddress() instanceof Inet4Address)) { + throw new IllegalArgumentException( + "Only IPv4 addresses can be used for the IP configuration"); + } mIpAddress = ipAddress; return this; } @@ -143,6 +138,10 @@ public final class StaticIpConfiguration implements Parcelable { * @return The {@link Builder} for chaining. */ public @NonNull Builder setGateway(@Nullable InetAddress gateway) { + if (gateway != null && !(gateway instanceof Inet4Address)) { + throw new IllegalArgumentException( + "Only IPv4 addresses can be used for the gateway configuration"); + } mGateway = gateway; return this; } @@ -153,6 +152,12 @@ public final class StaticIpConfiguration implements Parcelable { */ public @NonNull Builder setDnsServers(@NonNull Iterable dnsServers) { Objects.requireNonNull(dnsServers); + for (InetAddress inetAddress: dnsServers) { + if (!(inetAddress instanceof Inet4Address)) { + throw new IllegalArgumentException( + "Only IPv4 addresses can be used for the DNS server configuration"); + } + } mDnsServers = dnsServers; return this; } @@ -171,6 +176,8 @@ public final class StaticIpConfiguration implements Parcelable { /** * Create a {@link StaticIpConfiguration} from the parameters in this {@link Builder}. * @return The newly created StaticIpConfiguration. + * @throws IllegalArgumentException if an invalid configuration is attempted, e.g. + * if an IP Address was not configured via {@link #setIpAddress(LinkAddress)}. */ public @NonNull StaticIpConfiguration build() { final StaticIpConfiguration config = new StaticIpConfiguration(); @@ -188,7 +195,9 @@ public final class StaticIpConfiguration implements Parcelable { /** * Add a DNS server to this configuration. + * @hide */ + @SystemApi public void addDnsServer(@NonNull InetAddress server) { dnsServers.add(server); } @@ -197,7 +206,9 @@ public final class StaticIpConfiguration implements Parcelable { * Returns the network routes specified by this object. Will typically include a * directly-connected route for the IP address's local subnet and a default route. * @param iface Interface to include in the routes. + * @hide */ + @SystemApi public @NonNull List getRoutes(@Nullable String iface) { List routes = new ArrayList(3); if (ipAddress != null) { @@ -305,7 +316,7 @@ public final class StaticIpConfiguration implements Parcelable { /** Implement the Parcelable interface */ @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeParcelable(ipAddress, flags); InetAddressUtils.parcelInetAddress(dest, gateway, flags); dest.writeInt(dnsServers.size()); @@ -316,7 +327,7 @@ public final class StaticIpConfiguration implements Parcelable { } /** @hide */ - public static StaticIpConfiguration readFromParcel(Parcel in) { + public static @NonNull StaticIpConfiguration readFromParcel(Parcel in) { final StaticIpConfiguration s = new StaticIpConfiguration(); s.ipAddress = in.readParcelable(null); s.gateway = InetAddressUtils.unparcelInetAddress(in); diff --git a/tests/cts/net/src/android/net/cts/IpConfigurationTest.java b/tests/cts/net/src/android/net/cts/IpConfigurationTest.java index 385bf9e27f..d221694113 100644 --- a/tests/cts/net/src/android/net/cts/IpConfigurationTest.java +++ b/tests/cts/net/src/android/net/cts/IpConfigurationTest.java @@ -25,12 +25,16 @@ import android.net.IpConfiguration; import android.net.LinkAddress; import android.net.ProxyInfo; import android.net.StaticIpConfiguration; +import android.os.Build; import androidx.test.runner.AndroidJUnit4; +import com.android.testutils.DevSdkIgnoreRule; + import libcore.net.InetAddressUtils; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -50,6 +54,9 @@ public final class IpConfigurationTest { private StaticIpConfiguration mStaticIpConfig; private ProxyInfo mProxy; + @Rule + public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); + @Before public void setUp() { dnsServers.add(DNS1); @@ -99,6 +106,18 @@ public final class IpConfigurationTest { assertIpConfigurationEqual(ipConfig, new IpConfiguration(ipConfig)); } + @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) + @Test + public void testBuilder() { + final IpConfiguration c = new IpConfiguration.Builder() + .setStaticIpConfiguration(mStaticIpConfig) + .setHttpProxy(mProxy) + .build(); + + assertEquals(mStaticIpConfig, c.getStaticIpConfiguration()); + assertEquals(mProxy, c.getHttpProxy()); + } + private void checkEmpty(IpConfiguration config) { assertEquals(IpConfiguration.IpAssignment.UNASSIGNED, config.getIpAssignment().UNASSIGNED); diff --git a/tests/common/java/android/net/StaticIpConfigurationTest.java b/tests/cts/net/src/android/net/cts/StaticIpConfigurationTest.java similarity index 81% rename from tests/common/java/android/net/StaticIpConfigurationTest.java rename to tests/cts/net/src/android/net/cts/StaticIpConfigurationTest.java index b5f23bf19a..9b2756c8f0 100644 --- a/tests/common/java/android/net/StaticIpConfigurationTest.java +++ b/tests/cts/net/src/android/net/cts/StaticIpConfigurationTest.java @@ -14,20 +14,30 @@ * limitations under the License. */ -package android.net; +package android.net.cts; 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.assertNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.RouteInfo; +import android.net.StaticIpConfiguration; +import android.os.Build; import android.os.Parcel; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.testutils.DevSdkIgnoreRule; + +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -42,15 +52,20 @@ public class StaticIpConfigurationTest { private static final String ADDRSTR = "192.0.2.2/25"; private static final LinkAddress ADDR = new LinkAddress(ADDRSTR); - private static final InetAddress GATEWAY = IpAddress("192.0.2.1"); - private static final InetAddress OFFLINKGATEWAY = IpAddress("192.0.2.129"); - private static final InetAddress DNS1 = IpAddress("8.8.8.8"); - private static final InetAddress DNS2 = IpAddress("8.8.4.4"); - private static final InetAddress DNS3 = IpAddress("4.2.2.2"); + private static final InetAddress GATEWAY = ipAddress("192.0.2.1"); + private static final InetAddress OFFLINKGATEWAY = ipAddress("192.0.2.129"); + private static final InetAddress DNS1 = ipAddress("8.8.8.8"); + private static final InetAddress DNS2 = ipAddress("8.8.4.4"); + private static final InetAddress DNS3 = ipAddress("4.2.2.2"); + private static final InetAddress IPV6_ADDRESS = ipAddress("2001:4860:800d::68"); + private static final LinkAddress IPV6_LINK_ADDRESS = new LinkAddress("2001:db8::1/64"); private static final String IFACE = "eth0"; private static final String FAKE_DOMAINS = "google.com"; - private static InetAddress IpAddress(String addr) { + @Rule + public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); + + private static InetAddress ipAddress(String addr) { return InetAddress.parseNumericAddress(addr); } @@ -241,6 +256,29 @@ public class StaticIpConfigurationTest { assertEquals(DNS1, s.getDnsServers().get(0)); } + @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) + @Test + public void testIllegalBuilders() { + assertThrows("Can't set IP Address to IPv6!", IllegalArgumentException.class, () -> { + StaticIpConfiguration.Builder b = new StaticIpConfiguration.Builder().setIpAddress( + IPV6_LINK_ADDRESS); + }); + + assertThrows("Can't set gateway to IPv6!", IllegalArgumentException.class, () -> { + StaticIpConfiguration.Builder b = new StaticIpConfiguration.Builder().setGateway( + IPV6_ADDRESS); + }); + + assertThrows("Can't set DNS servers using IPv6!", IllegalArgumentException.class, () -> { + final ArrayList dnsServers = new ArrayList<>(); + dnsServers.add(DNS1); + dnsServers.add(IPV6_ADDRESS); + + StaticIpConfiguration.Builder b = new StaticIpConfiguration.Builder().setDnsServers( + dnsServers); + }); + } + @Test public void testAddDnsServers() { final StaticIpConfiguration s = new StaticIpConfiguration((StaticIpConfiguration) null);