diff options
21 files changed, 1386 insertions, 36 deletions
diff --git a/Android.bp b/Android.bp index 580df85f7dad..81a98502d519 100644 --- a/Android.bp +++ b/Android.bp @@ -824,17 +824,23 @@ aidl_interface { name: "networkstack-aidl-interfaces", local_include_dir: "core/java", srcs: [ + "core/java/android/net/ApfCapabilitiesParcelable.aidl", + "core/java/android/net/DhcpResultsParcelable.aidl", "core/java/android/net/INetworkMonitor.aidl", "core/java/android/net/INetworkMonitorCallbacks.aidl", "core/java/android/net/IIpMemoryStore.aidl", "core/java/android/net/INetworkStackConnector.aidl", "core/java/android/net/INetworkStackStatusCallback.aidl", + "core/java/android/net/InitialConfigurationParcelable.aidl", "core/java/android/net/IpPrefixParcelable.aidl", "core/java/android/net/LinkAddressParcelable.aidl", "core/java/android/net/LinkPropertiesParcelable.aidl", + "core/java/android/net/NetworkParcelable.aidl", "core/java/android/net/PrivateDnsConfigParcel.aidl", + "core/java/android/net/ProvisioningConfigurationParcelable.aidl", "core/java/android/net/ProxyInfoParcelable.aidl", "core/java/android/net/RouteInfoParcelable.aidl", + "core/java/android/net/StaticIpConfigurationParcelable.aidl", "core/java/android/net/dhcp/DhcpServingParamsParcel.aidl", "core/java/android/net/dhcp/IDhcpServer.aidl", "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl", diff --git a/core/java/android/net/ApfCapabilitiesParcelable.aidl b/core/java/android/net/ApfCapabilitiesParcelable.aidl new file mode 100644 index 000000000000..f0645d2782d2 --- /dev/null +++ b/core/java/android/net/ApfCapabilitiesParcelable.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +parcelable ApfCapabilitiesParcelable { + int apfVersionSupported; + int maximumApfProgramSize; + int apfPacketFormat; +}
\ No newline at end of file diff --git a/core/java/android/net/DhcpResultsParcelable.aidl b/core/java/android/net/DhcpResultsParcelable.aidl new file mode 100644 index 000000000000..cf5629b6f792 --- /dev/null +++ b/core/java/android/net/DhcpResultsParcelable.aidl @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ + +package android.net; + +import android.net.StaticIpConfigurationParcelable; + +parcelable DhcpResultsParcelable { + StaticIpConfigurationParcelable baseConfiguration; + int leaseDuration; + int mtu; + String serverAddress; + String vendorInfo; +}
\ No newline at end of file diff --git a/core/java/android/net/InitialConfigurationParcelable.aidl b/core/java/android/net/InitialConfigurationParcelable.aidl new file mode 100644 index 000000000000..bdda355955a5 --- /dev/null +++ b/core/java/android/net/InitialConfigurationParcelable.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.net.IpPrefixParcelable; +import android.net.LinkAddressParcelable; + +parcelable InitialConfigurationParcelable { + LinkAddressParcelable[] ipAddresses; + IpPrefixParcelable[] directlyConnectedRoutes; + String[] dnsServers; + String gateway; +}
\ No newline at end of file diff --git a/core/java/android/net/NetworkParcelable.aidl b/core/java/android/net/NetworkParcelable.aidl new file mode 100644 index 000000000000..c26352efb078 --- /dev/null +++ b/core/java/android/net/NetworkParcelable.aidl @@ -0,0 +1,22 @@ +/* +** +** Copyright (C) 2019 The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.net; + +parcelable NetworkParcelable { + long networkHandle; +} diff --git a/core/java/android/net/ProvisioningConfigurationParcelable.aidl b/core/java/android/net/ProvisioningConfigurationParcelable.aidl new file mode 100644 index 000000000000..2a144f2aae3b --- /dev/null +++ b/core/java/android/net/ProvisioningConfigurationParcelable.aidl @@ -0,0 +1,38 @@ +/* +** +** Copyright (C) 2019 The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.net; + +import android.net.ApfCapabilitiesParcelable; +import android.net.InitialConfigurationParcelable; +import android.net.NetworkParcelable; +import android.net.StaticIpConfigurationParcelable; + +parcelable ProvisioningConfigurationParcelable { + boolean enableIPv4; + boolean enableIPv6; + boolean usingMultinetworkPolicyTracker; + boolean usingIpReachabilityMonitor; + int requestedPreDhcpActionMs; + InitialConfigurationParcelable initialConfig; + StaticIpConfigurationParcelable staticIpConfig; + ApfCapabilitiesParcelable apfCapabilities; + int provisioningTimeoutMs; + int ipv6AddrGenMode; + NetworkParcelable network; + String displayName; +} diff --git a/core/java/android/net/StaticIpConfigurationParcelable.aidl b/core/java/android/net/StaticIpConfigurationParcelable.aidl new file mode 100644 index 000000000000..45dc0210dfba --- /dev/null +++ b/core/java/android/net/StaticIpConfigurationParcelable.aidl @@ -0,0 +1,27 @@ +/* +** +** Copyright (C) 2019 The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.net; + +import android.net.LinkAddressParcelable; + +parcelable StaticIpConfigurationParcelable { + LinkAddressParcelable ipAddress; + String gateway; + String[] dnsServers; + String domains; +} diff --git a/services/net/java/android/net/apf/ApfCapabilities.java b/core/java/android/net/apf/ApfCapabilities.java index dec8ca207343..f28cdc902848 100644 --- a/services/net/java/android/net/apf/ApfCapabilities.java +++ b/core/java/android/net/apf/ApfCapabilities.java @@ -38,18 +38,28 @@ public class ApfCapabilities { */ public final int apfPacketFormat; - public ApfCapabilities(int apfVersionSupported, int maximumApfProgramSize, int apfPacketFormat) - { + public ApfCapabilities( + int apfVersionSupported, int maximumApfProgramSize, int apfPacketFormat) { this.apfVersionSupported = apfVersionSupported; this.maximumApfProgramSize = maximumApfProgramSize; this.apfPacketFormat = apfPacketFormat; } + @Override public String toString() { return String.format("%s{version: %d, maxSize: %d, format: %d}", getClass().getSimpleName(), apfVersionSupported, maximumApfProgramSize, apfPacketFormat); } + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ApfCapabilities)) return false; + final ApfCapabilities other = (ApfCapabilities) obj; + return apfVersionSupported == other.apfVersionSupported + && maximumApfProgramSize == other.maximumApfProgramSize + && apfPacketFormat == other.apfPacketFormat; + } + /** * Returns true if the APF interpreter advertises support for the data buffer access opcodes * LDDW and STDW. diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index b0adf954cab4..2e7cbc684c3e 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -955,12 +955,64 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public long getIfaceStats(String iface, int type) { - return nativeGetIfaceStat(iface, type, checkBpfStatsEnable()); + long nativeIfaceStats = nativeGetIfaceStat(iface, type, checkBpfStatsEnable()); + if (nativeIfaceStats == -1) { + return nativeIfaceStats; + } else { + // When tethering offload is in use, nativeIfaceStats does not contain usage from + // offload, add it back here. + // When tethering offload is not in use, nativeIfaceStats contains tethering usage. + // this does not cause double-counting of tethering traffic, because + // NetdTetheringStatsProvider returns zero NetworkStats + // when called with STATS_PER_IFACE. + return nativeIfaceStats + getTetherStats(iface, type); + } } @Override public long getTotalStats(int type) { - return nativeGetTotalStat(type, checkBpfStatsEnable()); + long nativeTotalStats = nativeGetTotalStat(type, checkBpfStatsEnable()); + if (nativeTotalStats == -1) { + return nativeTotalStats; + } else { + // Refer to comment in getIfaceStats + return nativeTotalStats + getTetherStats(IFACE_ALL, type); + } + } + + private long getTetherStats(String iface, int type) { + final NetworkStats tetherSnapshot; + final long token = Binder.clearCallingIdentity(); + try { + tetherSnapshot = getNetworkStatsTethering(STATS_PER_IFACE); + } catch (RemoteException e) { + Slog.w(TAG, "Error get TetherStats: " + e); + return 0; + } finally { + Binder.restoreCallingIdentity(token); + } + HashSet<String> limitIfaces; + if (iface == IFACE_ALL) { + limitIfaces = null; + } else { + limitIfaces = new HashSet<String>(); + limitIfaces.add(iface); + } + NetworkStats.Entry entry = tetherSnapshot.getTotal(null, limitIfaces); + if (LOGD) Slog.d(TAG, "TetherStats: iface=" + iface + " type=" + type + + " entry=" + entry); + switch (type) { + case 0: // TYPE_RX_BYTES + return entry.rxBytes; + case 1: // TYPE_RX_PACKETS + return entry.rxPackets; + case 2: // TYPE_TX_BYTES + return entry.txBytes; + case 3: // TYPE_TX_PACKETS + return entry.txPackets; + default: + return 0; + } } private boolean checkBpfStatsEnable() { diff --git a/services/net/java/android/net/shared/InitialConfiguration.java b/services/net/java/android/net/shared/InitialConfiguration.java new file mode 100644 index 000000000000..bc2373f4aabc --- /dev/null +++ b/services/net/java/android/net/shared/InitialConfiguration.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.shared; + +import static android.net.shared.ParcelableUtil.fromParcelableArray; +import static android.net.shared.ParcelableUtil.toParcelableArray; +import static android.text.TextUtils.join; + +import android.net.InitialConfigurationParcelable; +import android.net.IpPrefix; +import android.net.IpPrefixParcelable; +import android.net.LinkAddress; +import android.net.LinkAddressParcelable; +import android.net.RouteInfo; + +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; + +/** @hide */ +public class InitialConfiguration { + public final Set<LinkAddress> ipAddresses = new HashSet<>(); + public final Set<IpPrefix> directlyConnectedRoutes = new HashSet<>(); + public final Set<InetAddress> dnsServers = new HashSet<>(); + + private static final int RFC6177_MIN_PREFIX_LENGTH = 48; + private static final int RFC7421_PREFIX_LENGTH = 64; + + /** + * Create a InitialConfiguration that is a copy of the specified configuration. + */ + public static InitialConfiguration copy(InitialConfiguration config) { + if (config == null) { + return null; + } + InitialConfiguration configCopy = new InitialConfiguration(); + configCopy.ipAddresses.addAll(config.ipAddresses); + configCopy.directlyConnectedRoutes.addAll(config.directlyConnectedRoutes); + configCopy.dnsServers.addAll(config.dnsServers); + return configCopy; + } + + @Override + public String toString() { + return String.format( + "InitialConfiguration(IPs: {%s}, prefixes: {%s}, DNS: {%s})", + join(", ", ipAddresses), join(", ", directlyConnectedRoutes), + join(", ", dnsServers)); + } + + /** + * Tests whether the contents of this IpConfiguration represent a valid configuration. + */ + public boolean isValid() { + if (ipAddresses.isEmpty()) { + return false; + } + + // For every IP address, there must be at least one prefix containing that address. + for (LinkAddress addr : ipAddresses) { + if (!any(directlyConnectedRoutes, (p) -> p.contains(addr.getAddress()))) { + return false; + } + } + // For every dns server, there must be at least one prefix containing that address. + for (InetAddress addr : dnsServers) { + if (!any(directlyConnectedRoutes, (p) -> p.contains(addr))) { + return false; + } + } + // All IPv6 LinkAddresses have an RFC7421-suitable prefix length + // (read: compliant with RFC4291#section2.5.4). + if (any(ipAddresses, not(InitialConfiguration::isPrefixLengthCompliant))) { + return false; + } + // If directlyConnectedRoutes contains an IPv6 default route + // then ipAddresses MUST contain at least one non-ULA GUA. + if (any(directlyConnectedRoutes, InitialConfiguration::isIPv6DefaultRoute) + && all(ipAddresses, not(InitialConfiguration::isIPv6GUA))) { + return false; + } + // The prefix length of routes in directlyConnectedRoutes be within reasonable + // bounds for IPv6: /48-/64 just as we’d accept in RIOs. + if (any(directlyConnectedRoutes, not(InitialConfiguration::isPrefixLengthCompliant))) { + return false; + } + // There no more than one IPv4 address + if (ipAddresses.stream().filter(LinkAddress::isIPv4).count() > 1) { + return false; + } + + return true; + } + + /** + * @return true if the given list of addressess and routes satisfies provisioning for this + * InitialConfiguration. LinkAddresses and RouteInfo objects are not compared with equality + * because addresses and routes seen by Netlink will contain additional fields like flags, + * interfaces, and so on. If this InitialConfiguration has no IP address specified, the + * provisioning check always fails. + * + * If the given list of routes is null, only addresses are taken into considerations. + */ + public boolean isProvisionedBy(List<LinkAddress> addresses, List<RouteInfo> routes) { + if (ipAddresses.isEmpty()) { + return false; + } + + for (LinkAddress addr : ipAddresses) { + if (!any(addresses, (addrSeen) -> addr.isSameAddressAs(addrSeen))) { + return false; + } + } + + if (routes != null) { + for (IpPrefix prefix : directlyConnectedRoutes) { + if (!any(routes, (routeSeen) -> isDirectlyConnectedRoute(routeSeen, prefix))) { + return false; + } + } + } + + return true; + } + + /** + * Convert this configuration to a {@link InitialConfigurationParcelable}. + */ + public InitialConfigurationParcelable toStableParcelable() { + final InitialConfigurationParcelable p = new InitialConfigurationParcelable(); + p.ipAddresses = toParcelableArray(ipAddresses, + LinkPropertiesParcelableUtil::toStableParcelable, LinkAddressParcelable.class); + p.directlyConnectedRoutes = toParcelableArray(directlyConnectedRoutes, + LinkPropertiesParcelableUtil::toStableParcelable, IpPrefixParcelable.class); + p.dnsServers = toParcelableArray( + dnsServers, IpConfigurationParcelableUtil::parcelAddress, String.class); + return p; + } + + /** + * Create an instance of {@link InitialConfiguration} based on the contents of the specified + * {@link InitialConfigurationParcelable}. + */ + public static InitialConfiguration fromStableParcelable(InitialConfigurationParcelable p) { + if (p == null) return null; + final InitialConfiguration config = new InitialConfiguration(); + config.ipAddresses.addAll(fromParcelableArray( + p.ipAddresses, LinkPropertiesParcelableUtil::fromStableParcelable)); + config.directlyConnectedRoutes.addAll(fromParcelableArray( + p.directlyConnectedRoutes, LinkPropertiesParcelableUtil::fromStableParcelable)); + config.dnsServers.addAll( + fromParcelableArray(p.dnsServers, IpConfigurationParcelableUtil::unparcelAddress)); + return config; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof InitialConfiguration)) return false; + final InitialConfiguration other = (InitialConfiguration) obj; + return ipAddresses.equals(other.ipAddresses) + && directlyConnectedRoutes.equals(other.directlyConnectedRoutes) + && dnsServers.equals(other.dnsServers); + } + + private static boolean isDirectlyConnectedRoute(RouteInfo route, IpPrefix prefix) { + return !route.hasGateway() && prefix.equals(route.getDestination()); + } + + private static boolean isPrefixLengthCompliant(LinkAddress addr) { + return addr.isIPv4() || isCompliantIPv6PrefixLength(addr.getPrefixLength()); + } + + private static boolean isPrefixLengthCompliant(IpPrefix prefix) { + return prefix.isIPv4() || isCompliantIPv6PrefixLength(prefix.getPrefixLength()); + } + + private static boolean isCompliantIPv6PrefixLength(int prefixLength) { + return (RFC6177_MIN_PREFIX_LENGTH <= prefixLength) + && (prefixLength <= RFC7421_PREFIX_LENGTH); + } + + private static boolean isIPv6DefaultRoute(IpPrefix prefix) { + return prefix.getAddress().equals(Inet6Address.ANY); + } + + private static boolean isIPv6GUA(LinkAddress addr) { + return addr.isIPv6() && addr.isGlobalPreferred(); + } + + // TODO: extract out into CollectionUtils. + + /** + * Indicate whether any element of the specified iterable verifies the specified predicate. + */ + public static <T> boolean any(Iterable<T> coll, Predicate<T> fn) { + for (T t : coll) { + if (fn.test(t)) { + return true; + } + } + return false; + } + + /** + * Indicate whether all elements of the specified iterable verifies the specified predicate. + */ + public static <T> boolean all(Iterable<T> coll, Predicate<T> fn) { + return !any(coll, not(fn)); + } + + /** + * Create a predicate that returns the opposite value of the specified predicate. + */ + public static <T> Predicate<T> not(Predicate<T> fn) { + return (t) -> !fn.test(t); + } +} diff --git a/services/net/java/android/net/shared/IpConfigurationParcelableUtil.java b/services/net/java/android/net/shared/IpConfigurationParcelableUtil.java new file mode 100644 index 000000000000..2c368c81523e --- /dev/null +++ b/services/net/java/android/net/shared/IpConfigurationParcelableUtil.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.shared; + +import static android.net.shared.ParcelableUtil.fromParcelableArray; +import static android.net.shared.ParcelableUtil.toParcelableArray; + +import android.annotation.Nullable; +import android.net.ApfCapabilitiesParcelable; +import android.net.DhcpResults; +import android.net.DhcpResultsParcelable; +import android.net.InetAddresses; +import android.net.StaticIpConfiguration; +import android.net.StaticIpConfigurationParcelable; +import android.net.apf.ApfCapabilities; + +import java.net.Inet4Address; +import java.net.InetAddress; + +/** + * Collection of utility methods to convert to and from stable AIDL parcelables for IpClient + * configuration classes. + * @hide + */ +public final class IpConfigurationParcelableUtil { + /** + * Convert a StaticIpConfiguration to a StaticIpConfigurationParcelable. + */ + public static StaticIpConfigurationParcelable toStableParcelable( + @Nullable StaticIpConfiguration config) { + if (config == null) return null; + final StaticIpConfigurationParcelable p = new StaticIpConfigurationParcelable(); + p.ipAddress = LinkPropertiesParcelableUtil.toStableParcelable(config.ipAddress); + p.gateway = parcelAddress(config.gateway); + p.dnsServers = toParcelableArray( + config.dnsServers, IpConfigurationParcelableUtil::parcelAddress, String.class); + p.domains = config.domains; + return p; + } + + /** + * Convert a StaticIpConfigurationParcelable to a StaticIpConfiguration. + */ + public static StaticIpConfiguration fromStableParcelable( + @Nullable StaticIpConfigurationParcelable p) { + if (p == null) return null; + final StaticIpConfiguration config = new StaticIpConfiguration(); + config.ipAddress = LinkPropertiesParcelableUtil.fromStableParcelable(p.ipAddress); + config.gateway = unparcelAddress(p.gateway); + config.dnsServers.addAll(fromParcelableArray( + p.dnsServers, IpConfigurationParcelableUtil::unparcelAddress)); + config.domains = p.domains; + return config; + } + + /** + * Convert DhcpResults to a DhcpResultsParcelable. + */ + public static DhcpResultsParcelable toStableParcelable(@Nullable DhcpResults results) { + if (results == null) return null; + final DhcpResultsParcelable p = new DhcpResultsParcelable(); + p.baseConfiguration = toStableParcelable((StaticIpConfiguration) results); + p.leaseDuration = results.leaseDuration; + p.mtu = results.mtu; + p.serverAddress = parcelAddress(results.serverAddress); + p.vendorInfo = results.vendorInfo; + return p; + } + + /** + * Convert a DhcpResultsParcelable to DhcpResults. + */ + public static DhcpResults fromStableParcelable(@Nullable DhcpResultsParcelable p) { + if (p == null) return null; + final DhcpResults results = new DhcpResults(fromStableParcelable(p.baseConfiguration)); + results.leaseDuration = p.leaseDuration; + results.mtu = p.mtu; + results.serverAddress = (Inet4Address) unparcelAddress(p.serverAddress); + results.vendorInfo = p.vendorInfo; + return results; + } + + /** + * Convert ApfCapabilities to ApfCapabilitiesParcelable. + */ + public static ApfCapabilitiesParcelable toStableParcelable(@Nullable ApfCapabilities caps) { + if (caps == null) return null; + final ApfCapabilitiesParcelable p = new ApfCapabilitiesParcelable(); + p.apfVersionSupported = caps.apfVersionSupported; + p.maximumApfProgramSize = caps.maximumApfProgramSize; + p.apfPacketFormat = caps.apfPacketFormat; + return p; + } + + /** + * Convert ApfCapabilitiesParcelable toApfCapabilities. + */ + public static ApfCapabilities fromStableParcelable(@Nullable ApfCapabilitiesParcelable p) { + if (p == null) return null; + return new ApfCapabilities( + p.apfVersionSupported, p.maximumApfProgramSize, p.apfPacketFormat); + } + + /** + * Convert InetAddress to String. + * TODO: have an InetAddressParcelable + */ + public static String parcelAddress(@Nullable InetAddress addr) { + if (addr == null) return null; + return addr.getHostAddress(); + } + + /** + * Convert String to InetAddress. + * TODO: have an InetAddressParcelable + */ + public static InetAddress unparcelAddress(@Nullable String addr) { + if (addr == null) return null; + return InetAddresses.parseNumericAddress(addr); + } +} diff --git a/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java index 5b77f543c62b..d5213dfcebf8 100644 --- a/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java +++ b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java @@ -16,11 +16,12 @@ package android.net.shared; +import static android.net.shared.IpConfigurationParcelableUtil.parcelAddress; +import static android.net.shared.IpConfigurationParcelableUtil.unparcelAddress; import static android.net.shared.ParcelableUtil.fromParcelableArray; import static android.net.shared.ParcelableUtil.toParcelableArray; import android.annotation.Nullable; -import android.net.InetAddresses; import android.net.IpPrefix; import android.net.IpPrefixParcelable; import android.net.LinkAddress; @@ -33,7 +34,6 @@ import android.net.RouteInfo; import android.net.RouteInfoParcelable; import android.net.Uri; -import java.net.InetAddress; import java.util.Arrays; /** @@ -81,7 +81,7 @@ public final class LinkPropertiesParcelableUtil { return null; } final IpPrefixParcelable parcel = new IpPrefixParcelable(); - parcel.address = ipPrefix.getAddress().getHostAddress(); + parcel.address = parcelAddress(ipPrefix.getAddress()); parcel.prefixLength = ipPrefix.getPrefixLength(); return parcel; } @@ -93,7 +93,7 @@ public final class LinkPropertiesParcelableUtil { if (parcel == null) { return null; } - return new IpPrefix(InetAddresses.parseNumericAddress(parcel.address), parcel.prefixLength); + return new IpPrefix(unparcelAddress(parcel.address), parcel.prefixLength); } /** @@ -105,7 +105,7 @@ public final class LinkPropertiesParcelableUtil { } final RouteInfoParcelable parcel = new RouteInfoParcelable(); parcel.destination = toStableParcelable(routeInfo.getDestination()); - parcel.gatewayAddr = routeInfo.getGateway().getHostAddress(); + parcel.gatewayAddr = parcelAddress(routeInfo.getGateway()); parcel.ifaceName = routeInfo.getInterface(); parcel.type = routeInfo.getType(); return parcel; @@ -120,7 +120,7 @@ public final class LinkPropertiesParcelableUtil { } final IpPrefix destination = fromStableParcelable(parcel.destination); return new RouteInfo( - destination, InetAddresses.parseNumericAddress(parcel.gatewayAddr), + destination, unparcelAddress(parcel.gatewayAddr), parcel.ifaceName, parcel.type); } @@ -132,7 +132,7 @@ public final class LinkPropertiesParcelableUtil { return null; } final LinkAddressParcelable parcel = new LinkAddressParcelable(); - parcel.address = la.getAddress().getHostAddress(); + parcel.address = parcelAddress(la.getAddress()); parcel.prefixLength = la.getPrefixLength(); parcel.flags = la.getFlags(); parcel.scope = la.getScope(); @@ -147,7 +147,7 @@ public final class LinkPropertiesParcelableUtil { return null; } return new LinkAddress( - InetAddresses.parseNumericAddress(parcel.address), + unparcelAddress(parcel.address), parcel.prefixLength, parcel.flags, parcel.scope); @@ -167,11 +167,11 @@ public final class LinkPropertiesParcelableUtil { LinkPropertiesParcelableUtil::toStableParcelable, LinkAddressParcelable.class); parcel.dnses = toParcelableArray( - lp.getDnsServers(), InetAddress::getHostAddress, String.class); + lp.getDnsServers(), IpConfigurationParcelableUtil::parcelAddress, String.class); parcel.pcscfs = toParcelableArray( - lp.getPcscfServers(), InetAddress::getHostAddress, String.class); - parcel.validatedPrivateDnses = toParcelableArray( - lp.getValidatedPrivateDnsServers(), InetAddress::getHostAddress, String.class); + lp.getPcscfServers(), IpConfigurationParcelableUtil::parcelAddress, String.class); + parcel.validatedPrivateDnses = toParcelableArray(lp.getValidatedPrivateDnsServers(), + IpConfigurationParcelableUtil::parcelAddress, String.class); parcel.usePrivateDns = lp.isPrivateDnsActive(); parcel.privateDnsServerName = lp.getPrivateDnsServerName(); parcel.domains = lp.getDomains(); @@ -199,11 +199,13 @@ public final class LinkPropertiesParcelableUtil { lp.setInterfaceName(parcel.ifaceName); lp.setLinkAddresses(fromParcelableArray(parcel.linkAddresses, LinkPropertiesParcelableUtil::fromStableParcelable)); - lp.setDnsServers(fromParcelableArray(parcel.dnses, InetAddresses::parseNumericAddress)); - lp.setPcscfServers(fromParcelableArray(parcel.pcscfs, InetAddresses::parseNumericAddress)); + lp.setDnsServers(fromParcelableArray( + parcel.dnses, IpConfigurationParcelableUtil::unparcelAddress)); + lp.setPcscfServers(fromParcelableArray( + parcel.pcscfs, IpConfigurationParcelableUtil::unparcelAddress)); lp.setValidatedPrivateDnsServers( fromParcelableArray(parcel.validatedPrivateDnses, - InetAddresses::parseNumericAddress)); + IpConfigurationParcelableUtil::unparcelAddress)); lp.setUsePrivateDns(parcel.usePrivateDns); lp.setPrivateDnsServerName(parcel.privateDnsServerName); lp.setDomains(parcel.domains); diff --git a/services/net/java/android/net/shared/NetworkParcelableUtil.java b/services/net/java/android/net/shared/NetworkParcelableUtil.java new file mode 100644 index 000000000000..d0b54b8b81d0 --- /dev/null +++ b/services/net/java/android/net/shared/NetworkParcelableUtil.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.shared; + +import android.annotation.Nullable; +import android.net.Network; +import android.net.NetworkParcelable; + +/** + * Utility methods to convert to/from stable AIDL parcelables for network attribute classes. + * @hide + */ +public final class NetworkParcelableUtil { + /** + * Convert from a Network to a NetworkParcelable. + */ + public static NetworkParcelable toStableParcelable(@Nullable Network network) { + if (network == null) { + return null; + } + final NetworkParcelable p = new NetworkParcelable(); + p.networkHandle = network.getNetworkHandle(); + + return p; + } + + /** + * Convert from a NetworkParcelable to a Network. + */ + public static Network fromStableParcelable(@Nullable NetworkParcelable p) { + if (p == null) { + return null; + } + return Network.fromNetworkHandle(p.networkHandle); + } +} diff --git a/services/net/java/android/net/shared/ParcelableUtil.java b/services/net/java/android/net/shared/ParcelableUtil.java index a18976c9eee6..3f4030047938 100644 --- a/services/net/java/android/net/shared/ParcelableUtil.java +++ b/services/net/java/android/net/shared/ParcelableUtil.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import java.lang.reflect.Array; import java.util.ArrayList; -import java.util.List; +import java.util.Collection; import java.util.function.Function; /** @@ -36,7 +36,7 @@ public final class ParcelableUtil { * converter function. */ public static <ParcelableType, BaseType> ParcelableType[] toParcelableArray( - @NonNull List<BaseType> base, + @NonNull Collection<BaseType> base, @NonNull Function<BaseType, ParcelableType> conv, @NonNull Class<ParcelableType> parcelClass) { final ParcelableType[] out = (ParcelableType[]) Array.newInstance(parcelClass, base.size()); diff --git a/services/net/java/android/net/shared/PrivateDnsConfig.java b/services/net/java/android/net/shared/PrivateDnsConfig.java index 41e0bad7d746..c7dc5306b51b 100644 --- a/services/net/java/android/net/shared/PrivateDnsConfig.java +++ b/services/net/java/android/net/shared/PrivateDnsConfig.java @@ -16,7 +16,9 @@ package android.net.shared; -import android.net.InetAddresses; +import static android.net.shared.ParcelableUtil.fromParcelableArray; +import static android.net.shared.ParcelableUtil.toParcelableArray; + import android.net.PrivateDnsConfigParcel; import android.text.TextUtils; @@ -70,12 +72,8 @@ public class PrivateDnsConfig { public PrivateDnsConfigParcel toParcel() { final PrivateDnsConfigParcel parcel = new PrivateDnsConfigParcel(); parcel.hostname = hostname; - - final String[] parceledIps = new String[ips.length]; - for (int i = 0; i < ips.length; i++) { - parceledIps[i] = ips[i].getHostAddress(); - } - parcel.ips = parceledIps; + parcel.ips = toParcelableArray( + Arrays.asList(ips), IpConfigurationParcelableUtil::parcelAddress, String.class); return parcel; } @@ -84,11 +82,9 @@ public class PrivateDnsConfig { * Build a configuration from a stable AIDL-compatible parcel. */ public static PrivateDnsConfig fromParcel(PrivateDnsConfigParcel parcel) { - final InetAddress[] ips = new InetAddress[parcel.ips.length]; - for (int i = 0; i < ips.length; i++) { - ips[i] = InetAddresses.parseNumericAddress(parcel.ips[i]); - } - + InetAddress[] ips = new InetAddress[parcel.ips.length]; + ips = fromParcelableArray(parcel.ips, IpConfigurationParcelableUtil::unparcelAddress) + .toArray(ips); return new PrivateDnsConfig(parcel.hostname, ips); } } diff --git a/services/net/java/android/net/shared/ProvisioningConfiguration.java b/services/net/java/android/net/shared/ProvisioningConfiguration.java new file mode 100644 index 000000000000..d995d1b1e622 --- /dev/null +++ b/services/net/java/android/net/shared/ProvisioningConfiguration.java @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.shared; + +import android.annotation.Nullable; +import android.net.INetd; +import android.net.Network; +import android.net.ProvisioningConfigurationParcelable; +import android.net.StaticIpConfiguration; +import android.net.apf.ApfCapabilities; + +import java.util.Objects; +import java.util.StringJoiner; + +/** + * This class encapsulates parameters to be passed to + * IpClient#startProvisioning(). A defensive copy is made by IpClient + * and the values specified herein are in force until IpClient#stop() + * is called. + * + * Example use: + * + * final ProvisioningConfiguration config = + * new ProvisioningConfiguration.Builder() + * .withPreDhcpAction() + * .withProvisioningTimeoutMs(36 * 1000) + * .build(); + * mIpClient.startProvisioning(config.toStableParcelable()); + * ... + * mIpClient.stop(); + * + * The specified provisioning configuration will only be active until + * IIpClient#stop() is called. Future calls to IIpClient#startProvisioning() + * must specify the configuration again. + * @hide + */ +public class ProvisioningConfiguration { + // TODO: Delete this default timeout once those callers that care are + // fixed to pass in their preferred timeout. + // + // We pick 36 seconds so we can send DHCP requests at + // + // t=0, t=2, t=6, t=14, t=30 + // + // allowing for 10% jitter. + private static final int DEFAULT_TIMEOUT_MS = 36 * 1000; + + /** + * Builder to create a {@link ProvisioningConfiguration}. + */ + public static class Builder { + protected ProvisioningConfiguration mConfig = new ProvisioningConfiguration(); + + /** + * Specify that the configuration should not enable IPv4. It is enabled by default. + */ + public Builder withoutIPv4() { + mConfig.mEnableIPv4 = false; + return this; + } + + /** + * Specify that the configuration should not enable IPv6. It is enabled by default. + */ + public Builder withoutIPv6() { + mConfig.mEnableIPv6 = false; + return this; + } + + /** + * Specify that the configuration should not use a MultinetworkPolicyTracker. It is used + * by default. + */ + public Builder withoutMultinetworkPolicyTracker() { + mConfig.mUsingMultinetworkPolicyTracker = false; + return this; + } + + /** + * Specify that the configuration should not use a IpReachabilityMonitor. It is used by + * default. + */ + public Builder withoutIpReachabilityMonitor() { + mConfig.mUsingIpReachabilityMonitor = false; + return this; + } + + /** + * Identical to {@link #withPreDhcpAction(int)}, using a default timeout. + * @see #withPreDhcpAction(int) + */ + public Builder withPreDhcpAction() { + mConfig.mRequestedPreDhcpActionMs = DEFAULT_TIMEOUT_MS; + return this; + } + + /** + * Specify that {@link IpClientCallbacks#onPreDhcpAction()} should be called. Clients must + * call {@link IIpClient#completedPreDhcpAction()} when the callback called. This behavior + * is disabled by default. + * @param dhcpActionTimeoutMs Timeout for clients to call completedPreDhcpAction(). + */ + public Builder withPreDhcpAction(int dhcpActionTimeoutMs) { + mConfig.mRequestedPreDhcpActionMs = dhcpActionTimeoutMs; + return this; + } + + /** + * Specify the initial provisioning configuration. + */ + public Builder withInitialConfiguration(InitialConfiguration initialConfig) { + mConfig.mInitialConfig = initialConfig; + return this; + } + + /** + * Specify a static configuration for provisioning. + */ + public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) { + mConfig.mStaticIpConfig = staticConfig; + return this; + } + + /** + * Specify ApfCapabilities. + */ + public Builder withApfCapabilities(ApfCapabilities apfCapabilities) { + mConfig.mApfCapabilities = apfCapabilities; + return this; + } + + /** + * Specify the timeout to use for provisioning. + */ + public Builder withProvisioningTimeoutMs(int timeoutMs) { + mConfig.mProvisioningTimeoutMs = timeoutMs; + return this; + } + + /** + * Specify that IPv6 address generation should use a random MAC address. + */ + public Builder withRandomMacAddress() { + mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_EUI64; + return this; + } + + /** + * Specify that IPv6 address generation should use a stable MAC address. + */ + public Builder withStableMacAddress() { + mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; + return this; + } + + /** + * Specify the network to use for provisioning. + */ + public Builder withNetwork(Network network) { + mConfig.mNetwork = network; + return this; + } + + /** + * Specify the display name that the IpClient should use. + */ + public Builder withDisplayName(String displayName) { + mConfig.mDisplayName = displayName; + return this; + } + + /** + * Build the configuration using previously specified parameters. + */ + public ProvisioningConfiguration build() { + return new ProvisioningConfiguration(mConfig); + } + } + + public boolean mEnableIPv4 = true; + public boolean mEnableIPv6 = true; + public boolean mUsingMultinetworkPolicyTracker = true; + public boolean mUsingIpReachabilityMonitor = true; + public int mRequestedPreDhcpActionMs; + public InitialConfiguration mInitialConfig; + public StaticIpConfiguration mStaticIpConfig; + public ApfCapabilities mApfCapabilities; + public int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS; + public int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; + public Network mNetwork = null; + public String mDisplayName = null; + + public ProvisioningConfiguration() {} // used by Builder + + public ProvisioningConfiguration(ProvisioningConfiguration other) { + mEnableIPv4 = other.mEnableIPv4; + mEnableIPv6 = other.mEnableIPv6; + mUsingMultinetworkPolicyTracker = other.mUsingMultinetworkPolicyTracker; + mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor; + mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs; + mInitialConfig = InitialConfiguration.copy(other.mInitialConfig); + mStaticIpConfig = other.mStaticIpConfig == null + ? null + : new StaticIpConfiguration(other.mStaticIpConfig); + mApfCapabilities = other.mApfCapabilities; + mProvisioningTimeoutMs = other.mProvisioningTimeoutMs; + mIPv6AddrGenMode = other.mIPv6AddrGenMode; + mNetwork = other.mNetwork; + mDisplayName = other.mDisplayName; + } + + /** + * Create a ProvisioningConfigurationParcelable from this ProvisioningConfiguration. + */ + public ProvisioningConfigurationParcelable toStableParcelable() { + final ProvisioningConfigurationParcelable p = new ProvisioningConfigurationParcelable(); + p.enableIPv4 = mEnableIPv4; + p.enableIPv6 = mEnableIPv6; + p.usingMultinetworkPolicyTracker = mUsingMultinetworkPolicyTracker; + p.usingIpReachabilityMonitor = mUsingIpReachabilityMonitor; + p.requestedPreDhcpActionMs = mRequestedPreDhcpActionMs; + p.initialConfig = mInitialConfig == null ? null : mInitialConfig.toStableParcelable(); + p.staticIpConfig = IpConfigurationParcelableUtil.toStableParcelable(mStaticIpConfig); + p.apfCapabilities = IpConfigurationParcelableUtil.toStableParcelable(mApfCapabilities); + p.provisioningTimeoutMs = mProvisioningTimeoutMs; + p.ipv6AddrGenMode = mIPv6AddrGenMode; + p.network = NetworkParcelableUtil.toStableParcelable(mNetwork); + p.displayName = mDisplayName; + return p; + } + + /** + * Create a ProvisioningConfiguration from a ProvisioningConfigurationParcelable. + */ + public static ProvisioningConfiguration fromStableParcelable( + @Nullable ProvisioningConfigurationParcelable p) { + if (p == null) return null; + final ProvisioningConfiguration config = new ProvisioningConfiguration(); + config.mEnableIPv4 = p.enableIPv4; + config.mEnableIPv6 = p.enableIPv6; + config.mUsingMultinetworkPolicyTracker = p.usingMultinetworkPolicyTracker; + config.mUsingIpReachabilityMonitor = p.usingIpReachabilityMonitor; + config.mRequestedPreDhcpActionMs = p.requestedPreDhcpActionMs; + config.mInitialConfig = InitialConfiguration.fromStableParcelable(p.initialConfig); + config.mStaticIpConfig = IpConfigurationParcelableUtil.fromStableParcelable( + p.staticIpConfig); + config.mApfCapabilities = IpConfigurationParcelableUtil.fromStableParcelable( + p.apfCapabilities); + config.mProvisioningTimeoutMs = p.provisioningTimeoutMs; + config.mIPv6AddrGenMode = p.ipv6AddrGenMode; + config.mNetwork = NetworkParcelableUtil.fromStableParcelable(p.network); + config.mDisplayName = p.displayName; + return config; + } + + @Override + public String toString() { + return new StringJoiner(", ", getClass().getSimpleName() + "{", "}") + .add("mEnableIPv4: " + mEnableIPv4) + .add("mEnableIPv6: " + mEnableIPv6) + .add("mUsingMultinetworkPolicyTracker: " + mUsingMultinetworkPolicyTracker) + .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor) + .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs) + .add("mInitialConfig: " + mInitialConfig) + .add("mStaticIpConfig: " + mStaticIpConfig) + .add("mApfCapabilities: " + mApfCapabilities) + .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs) + .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode) + .add("mNetwork: " + mNetwork) + .add("mDisplayName: " + mDisplayName) + .toString(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ProvisioningConfiguration)) return false; + final ProvisioningConfiguration other = (ProvisioningConfiguration) obj; + return mEnableIPv4 == other.mEnableIPv4 + && mEnableIPv6 == other.mEnableIPv6 + && mUsingMultinetworkPolicyTracker == other.mUsingMultinetworkPolicyTracker + && mUsingIpReachabilityMonitor == other.mUsingIpReachabilityMonitor + && mRequestedPreDhcpActionMs == other.mRequestedPreDhcpActionMs + && Objects.equals(mInitialConfig, other.mInitialConfig) + && Objects.equals(mStaticIpConfig, other.mStaticIpConfig) + && Objects.equals(mApfCapabilities, other.mApfCapabilities) + && mProvisioningTimeoutMs == other.mProvisioningTimeoutMs + && mIPv6AddrGenMode == other.mIPv6AddrGenMode + && Objects.equals(mNetwork, other.mNetwork) + && Objects.equals(mDisplayName, other.mDisplayName); + } + + public boolean isValid() { + return (mInitialConfig == null) || mInitialConfig.isValid(); + } +} diff --git a/tests/net/java/android/net/shared/InitialConfigurationTest.java b/tests/net/java/android/net/shared/InitialConfigurationTest.java new file mode 100644 index 000000000000..78792bdfe4f9 --- /dev/null +++ b/tests/net/java/android/net/shared/InitialConfigurationTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.shared; + +import static android.net.InetAddresses.parseNumericAddress; +import static android.net.shared.ParcelableTestUtil.assertFieldCountEquals; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; +import java.util.function.Consumer; + +/** + * Tests for {@link InitialConfiguration} + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class InitialConfigurationTest { + private InitialConfiguration mConfig; + + @Before + public void setUp() { + mConfig = new InitialConfiguration(); + mConfig.ipAddresses.addAll(Arrays.asList( + new LinkAddress(parseNumericAddress("192.168.45.45"), 16), + new LinkAddress(parseNumericAddress("2001:db8::45"), 33))); + mConfig.directlyConnectedRoutes.addAll(Arrays.asList( + new IpPrefix(parseNumericAddress("192.168.46.46"), 17), + new IpPrefix(parseNumericAddress("2001:db8::46"), 34))); + mConfig.dnsServers.addAll(Arrays.asList( + parseNumericAddress("192.168.47.47"), + parseNumericAddress("2001:db8::47"))); + // Any added InitialConfiguration field must be included in equals() to be tested properly + assertFieldCountEquals(3, InitialConfiguration.class); + } + + @Test + public void testParcelUnparcelInitialConfiguration() { + final InitialConfiguration unparceled = + InitialConfiguration.fromStableParcelable(mConfig.toStableParcelable()); + assertEquals(mConfig, unparceled); + } + + @Test + public void testEquals() { + assertEquals(mConfig, InitialConfiguration.copy(mConfig)); + + assertNotEqualsAfterChange(c -> c.ipAddresses.add( + new LinkAddress(parseNumericAddress("192.168.47.47"), 24))); + assertNotEqualsAfterChange(c -> c.directlyConnectedRoutes.add( + new IpPrefix(parseNumericAddress("192.168.46.46"), 32))); + assertNotEqualsAfterChange(c -> c.dnsServers.add(parseNumericAddress("2001:db8::49"))); + assertFieldCountEquals(3, InitialConfiguration.class); + } + + private void assertNotEqualsAfterChange(Consumer<InitialConfiguration> mutator) { + final InitialConfiguration newConfig = InitialConfiguration.copy(mConfig); + mutator.accept(newConfig); + assertNotEquals(mConfig, newConfig); + } +} diff --git a/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java b/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java new file mode 100644 index 000000000000..14df392cbe07 --- /dev/null +++ b/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.shared; + +import static android.net.InetAddresses.parseNumericAddress; +import static android.net.shared.IpConfigurationParcelableUtil.fromStableParcelable; +import static android.net.shared.IpConfigurationParcelableUtil.toStableParcelable; +import static android.net.shared.ParcelableTestUtil.assertFieldCountEquals; + +import static org.junit.Assert.assertEquals; + +import android.net.DhcpResults; +import android.net.LinkAddress; +import android.net.StaticIpConfiguration; +import android.net.apf.ApfCapabilities; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.Inet4Address; + +/** + * Tests for {@link IpConfigurationParcelableUtil}. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class IpConfigurationParcelableUtilTest { + private StaticIpConfiguration mStaticIpConfiguration; + private DhcpResults mDhcpResults; + + @Before + public void setUp() { + mStaticIpConfiguration = new StaticIpConfiguration(); + mStaticIpConfiguration.ipAddress = new LinkAddress(parseNumericAddress("2001:db8::42"), 64); + mStaticIpConfiguration.gateway = parseNumericAddress("192.168.42.42"); + mStaticIpConfiguration.dnsServers.add(parseNumericAddress("2001:db8::43")); + mStaticIpConfiguration.dnsServers.add(parseNumericAddress("192.168.43.43")); + mStaticIpConfiguration.domains = "example.com"; + // Any added StaticIpConfiguration field must be included in equals() to be tested properly + assertFieldCountEquals(4, StaticIpConfiguration.class); + + mDhcpResults = new DhcpResults(mStaticIpConfiguration); + mDhcpResults.serverAddress = (Inet4Address) parseNumericAddress("192.168.44.44"); + mDhcpResults.vendorInfo = "TEST_VENDOR_INFO"; + mDhcpResults.leaseDuration = 3600; + mDhcpResults.mtu = 1450; + // Any added DhcpResults field must be included in equals() to be tested properly + assertFieldCountEquals(4, DhcpResults.class); + } + + @Test + public void testParcelUnparcelStaticConfiguration() { + doStaticConfigurationParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcelStaticConfiguration_NullIpAddress() { + mStaticIpConfiguration.ipAddress = null; + doStaticConfigurationParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcelStaticConfiguration_NullGateway() { + mStaticIpConfiguration.gateway = null; + doStaticConfigurationParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcelStaticConfiguration_NullDomains() { + mStaticIpConfiguration.domains = null; + doStaticConfigurationParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcelStaticConfiguration_EmptyDomains() { + mStaticIpConfiguration.domains = ""; + doStaticConfigurationParcelUnparcelTest(); + } + + private void doStaticConfigurationParcelUnparcelTest() { + final StaticIpConfiguration unparceled = + fromStableParcelable(toStableParcelable(mStaticIpConfiguration)); + assertEquals(mStaticIpConfiguration, unparceled); + } + + @Test + public void testParcelUnparcelDhcpResults() { + doDhcpResultsParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcelDhcpResults_NullServerAddress() { + mDhcpResults.serverAddress = null; + doDhcpResultsParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcelDhcpResults_NullVendorInfo() { + mDhcpResults.vendorInfo = null; + doDhcpResultsParcelUnparcelTest(); + } + + private void doDhcpResultsParcelUnparcelTest() { + final DhcpResults unparceled = fromStableParcelable(toStableParcelable(mDhcpResults)); + assertEquals(mDhcpResults, unparceled); + } + + @Test + public void testParcelUnparcelApfCapabilities() { + final ApfCapabilities caps = new ApfCapabilities(123, 456, 789); + assertEquals(caps, fromStableParcelable(toStableParcelable(caps))); + } +} diff --git a/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java index 4cabfc95b49d..6f711c0b5743 100644 --- a/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java +++ b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java @@ -18,6 +18,7 @@ package android.net.shared; import static android.net.shared.LinkPropertiesParcelableUtil.fromStableParcelable; import static android.net.shared.LinkPropertiesParcelableUtil.toStableParcelable; +import static android.net.shared.ParcelableTestUtil.assertFieldCountEquals; import static org.junit.Assert.assertEquals; @@ -35,7 +36,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Collections; @@ -100,8 +100,7 @@ public class LinkPropertiesParcelableUtilTest { // Verify that this test does not miss any new field added later. // If any added field is not included in LinkProperties#equals, assertLinkPropertiesEquals // must also be updated. - assertEquals(14, Arrays.stream(LinkProperties.class.getDeclaredFields()) - .filter(f -> !Modifier.isStatic(f.getModifiers())).count()); + assertFieldCountEquals(14, LinkProperties.class); return lp; } diff --git a/tests/net/java/android/net/shared/ParcelableTestUtil.java b/tests/net/java/android/net/shared/ParcelableTestUtil.java new file mode 100644 index 000000000000..088ea3c1d1ed --- /dev/null +++ b/tests/net/java/android/net/shared/ParcelableTestUtil.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.shared; + +import static org.junit.Assert.assertEquals; + +import java.lang.reflect.Modifier; +import java.util.Arrays; + +/** + * Utility classes to write tests for stable AIDL parceling/unparceling + */ +public final class ParcelableTestUtil { + + /** + * Verifies that the number of nonstatic fields in a class equals a given count. + * + * <p>This assertion serves as a reminder to update test code around it if fields are added + * after the test is written. + * @param count Expected number of nonstatic fields in the class. + * @param clazz Class to test. + */ + public static <T> void assertFieldCountEquals(int count, Class<T> clazz) { + assertEquals(count, Arrays.stream(clazz.getDeclaredFields()) + .filter(f -> !Modifier.isStatic(f.getModifiers())).count()); + } +} diff --git a/tests/net/java/android/net/shared/ProvisioningConfigurationTest.java b/tests/net/java/android/net/shared/ProvisioningConfigurationTest.java new file mode 100644 index 000000000000..6ea47d2160a7 --- /dev/null +++ b/tests/net/java/android/net/shared/ProvisioningConfigurationTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.shared; + +import static android.net.InetAddresses.parseNumericAddress; +import static android.net.shared.ParcelableTestUtil.assertFieldCountEquals; +import static android.net.shared.ProvisioningConfiguration.fromStableParcelable; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.net.LinkAddress; +import android.net.Network; +import android.net.StaticIpConfiguration; +import android.net.apf.ApfCapabilities; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.function.Consumer; + +/** + * Tests for {@link ProvisioningConfiguration}. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ProvisioningConfigurationTest { + private ProvisioningConfiguration mConfig; + + @Before + public void setUp() { + mConfig = new ProvisioningConfiguration(); + mConfig.mEnableIPv4 = true; + mConfig.mEnableIPv6 = true; + mConfig.mUsingMultinetworkPolicyTracker = true; + mConfig.mUsingIpReachabilityMonitor = true; + mConfig.mRequestedPreDhcpActionMs = 42; + mConfig.mInitialConfig = new InitialConfiguration(); + mConfig.mInitialConfig.ipAddresses.add( + new LinkAddress(parseNumericAddress("192.168.42.42"), 24)); + mConfig.mStaticIpConfig = new StaticIpConfiguration(); + mConfig.mStaticIpConfig.ipAddress = + new LinkAddress(parseNumericAddress("2001:db8::42"), 90); + // Not testing other InitialConfig or StaticIpConfig members: they have their own unit tests + mConfig.mApfCapabilities = new ApfCapabilities(1, 2, 3); + mConfig.mProvisioningTimeoutMs = 4200; + mConfig.mIPv6AddrGenMode = 123; + mConfig.mNetwork = new Network(321); + mConfig.mDisplayName = "test_config"; + // Any added field must be included in equals() to be tested properly + assertFieldCountEquals(12, ProvisioningConfiguration.class); + } + + @Test + public void testParcelUnparcel() { + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullInitialConfiguration() { + mConfig.mInitialConfig = null; + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullStaticConfiguration() { + mConfig.mStaticIpConfig = null; + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullApfCapabilities() { + mConfig.mApfCapabilities = null; + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullNetwork() { + mConfig.mNetwork = null; + doParcelUnparcelTest(); + } + + private void doParcelUnparcelTest() { + final ProvisioningConfiguration unparceled = + fromStableParcelable(mConfig.toStableParcelable()); + assertEquals(mConfig, unparceled); + } + + @Test + public void testEquals() { + assertEquals(mConfig, new ProvisioningConfiguration(mConfig)); + + assertNotEqualsAfterChange(c -> c.mEnableIPv4 = false); + assertNotEqualsAfterChange(c -> c.mEnableIPv6 = false); + assertNotEqualsAfterChange(c -> c.mUsingMultinetworkPolicyTracker = false); + assertNotEqualsAfterChange(c -> c.mUsingIpReachabilityMonitor = false); + assertNotEqualsAfterChange(c -> c.mRequestedPreDhcpActionMs++); + assertNotEqualsAfterChange(c -> c.mInitialConfig.ipAddresses.add( + new LinkAddress(parseNumericAddress("192.168.47.47"), 16))); + assertNotEqualsAfterChange(c -> c.mInitialConfig = null); + assertNotEqualsAfterChange(c -> c.mStaticIpConfig.ipAddress = + new LinkAddress(parseNumericAddress("2001:db8::47"), 64)); + assertNotEqualsAfterChange(c -> c.mStaticIpConfig = null); + assertNotEqualsAfterChange(c -> c.mApfCapabilities = new ApfCapabilities(4, 5, 6)); + assertNotEqualsAfterChange(c -> c.mApfCapabilities = null); + assertNotEqualsAfterChange(c -> c.mProvisioningTimeoutMs++); + assertNotEqualsAfterChange(c -> c.mIPv6AddrGenMode++); + assertNotEqualsAfterChange(c -> c.mNetwork = new Network(123)); + assertNotEqualsAfterChange(c -> c.mNetwork = null); + assertNotEqualsAfterChange(c -> c.mDisplayName = "other_test"); + assertNotEqualsAfterChange(c -> c.mDisplayName = null); + assertFieldCountEquals(12, ProvisioningConfiguration.class); + } + + private void assertNotEqualsAfterChange(Consumer<ProvisioningConfiguration> mutator) { + final ProvisioningConfiguration newConfig = new ProvisioningConfiguration(mConfig); + mutator.accept(newConfig); + assertNotEquals(mConfig, newConfig); + } +} |