diff options
author | Steven Laver <lavers@google.com> | 2019-05-08 06:18:42 -0700 |
---|---|---|
committer | Steven Laver <lavers@google.com> | 2019-05-08 06:18:42 -0700 |
commit | 4a30c150e58ec67edb92a7bec5ea48cebd6b90d4 (patch) | |
tree | 6d902794d7e59acd54f54ef235feb75becfb01e3 /packages/NetworkStack | |
parent | a6c7c402af360214e509d15980957e74fc673f42 (diff) | |
parent | e4f4eabc31685c001c2b46d8fa87b443f4af5b21 (diff) |
Merge QP1A.190501.001
Change-Id: I0f9f887d6a33702a6988709d8296731cfdb8f73d
Diffstat (limited to 'packages/NetworkStack')
18 files changed, 539 insertions, 52 deletions
diff --git a/packages/NetworkStack/res/values-mcc460/config.xml b/packages/NetworkStack/res/values-mcc460/config.xml new file mode 100644 index 000000000000..fd4a8481ab22 --- /dev/null +++ b/packages/NetworkStack/res/values-mcc460/config.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Network validation URL configuration for devices using a Chinese SIM (MCC 460). + The below URLs are often whitelisted by captive portals, so they should not be used in the + general case as this could degrade the user experience (portals not detected properly). + However in China the default URLs are not accessible in general. The below alternatives + should allow users to connect to local networks normally. --> + <string name="default_captive_portal_https_url" translatable="false">https://connectivitycheck.gstatic.com/generate_204</string> + <string-array name="default_captive_portal_fallback_urls" translatable="false"> + <item>http://www.googleapis.cn/generate_204</item> + </string-array> +</resources> diff --git a/packages/NetworkStack/res/values/config.xml b/packages/NetworkStack/res/values/config.xml index 90f96e0f7a18..478ed6b06596 100644 --- a/packages/NetworkStack/res/values/config.xml +++ b/packages/NetworkStack/res/values/config.xml @@ -7,6 +7,9 @@ values are meant to be the default when no other configuration is specified. --> + <!-- DNS probe timeout for network validation. Enough for 3 DNS queries 5 seconds apart. --> + <integer name="default_captive_portal_dns_probe_timeout">12500</integer> + <!-- HTTP URL for network validation, to use for detecting captive portals. --> <string name="default_captive_portal_http_url" translatable="false">http://connectivitycheck.gstatic.com/generate_204</string> @@ -27,6 +30,7 @@ <!-- Configuration hooks for the above settings. Empty by default but may be overridden by RROs. --> + <integer name="config_captive_portal_dns_probe_timeout"></integer> <!--suppress CheckTagEmptyBody: overlayable resource to use as configuration hook --> <string name="config_captive_portal_http_url" translatable="false"></string> <!--suppress CheckTagEmptyBody: overlayable resource to use as configuration hook --> @@ -35,4 +39,8 @@ </string-array> <string-array name="config_captive_portal_fallback_probe_specs" translatable="false"> </string-array> -</resources>
\ No newline at end of file + + <!-- Customized default DNS Servers address. --> + <string-array name="config_default_dns_servers" translatable="false"> + </string-array> +</resources> diff --git a/packages/NetworkStack/res/values/overlayable.xml b/packages/NetworkStack/res/values/overlayable.xml new file mode 100644 index 000000000000..b9d5337ec572 --- /dev/null +++ b/packages/NetworkStack/res/values/overlayable.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- 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. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <overlayable name="NetworkStackConfig"> + <policy type="product|system|vendor"> + <!-- Configuration values for NetworkMonitor --> + <item type="integer" name="config_captive_portal_dns_probe_timeout"/> + <item type="string" name="config_captive_portal_http_url"/> + <item type="string" name="config_captive_portal_https_url"/> + <item type="array" name="config_captive_portal_fallback_urls"/> + <!-- Configuration value for DhcpResults --> + <item type="array" name="config_default_dns_servers"/> + </policy> + </overlayable> +</resources> diff --git a/packages/NetworkStack/src/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java index 663e2f10ffe2..359c85983a94 100644 --- a/packages/NetworkStack/src/android/net/apf/ApfFilter.java +++ b/packages/NetworkStack/src/android/net/apf/ApfFilter.java @@ -39,6 +39,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.NattKeepalivePacketDataParcelable; import android.net.TcpKeepalivePacketDataParcelable; import android.net.apf.ApfGenerator.IllegalInstructionException; import android.net.apf.ApfGenerator.Register; @@ -1691,13 +1692,13 @@ public class ApfFilter { } /** - * Add keepalive ack packet filter. + * Add TCP keepalive ack packet filter. * This will add a filter to drop acks to the keepalive packet passed as an argument. * * @param slot The index used to access the filter. * @param sentKeepalivePacket The attributes of the sent keepalive packet. */ - public synchronized void addKeepalivePacketFilter(final int slot, + public synchronized void addTcpKeepalivePacketFilter(final int slot, final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { log("Adding keepalive ack(" + slot + ")"); if (null != mKeepaliveAcks.get(slot)) { @@ -1711,6 +1712,18 @@ public class ApfFilter { } /** + * Add NATT keepalive packet filter. + * This will add a filter to drop NATT keepalive packet which is passed as an argument. + * + * @param slot The index used to access the filter. + * @param sentKeepalivePacket The attributes of the sent keepalive packet. + */ + public synchronized void addNattKeepalivePacketFilter(final int slot, + final NattKeepalivePacketDataParcelable sentKeepalivePacket) { + Log.e(TAG, "APF add NATT keepalive filter is not implemented"); + } + + /** * Remove keepalive packet filter. * * @param slot The index used to access the filter. diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java index 8e41ab1b5df4..c47231d80948 100644 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java +++ b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java @@ -45,6 +45,7 @@ import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY; import android.content.Context; import android.net.DhcpResults; +import android.net.InetAddresses; import android.net.TrafficStats; import android.net.ip.IpClient; import android.net.metrics.DhcpClientEvent; @@ -67,6 +68,7 @@ import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.TrafficStatsConstants; import com.android.internal.util.WakeupMessage; +import com.android.networkstack.R; import java.io.FileDescriptor; import java.io.IOException; @@ -516,6 +518,18 @@ public class DhcpClient extends StateMachine { private void acceptDhcpResults(DhcpResults results, String msg) { mDhcpLease = results; + if (mDhcpLease.dnsServers.isEmpty()) { + // supplement customized dns servers + String[] dnsServersList = + mContext.getResources().getStringArray(R.array.config_default_dns_servers); + for (final String dnsServer : dnsServersList) { + try { + mDhcpLease.dnsServers.add(InetAddresses.parseNumericAddress(dnsServer)); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Invalid default DNS server: " + dnsServer, e); + } + } + } mOffer = null; Log.d(TAG, msg + " lease: " + mDhcpLease); notifySuccess(); diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java index b4ebf264608b..c382733041e0 100644 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java +++ b/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java @@ -195,6 +195,18 @@ public abstract class DhcpPacket { public static final String VENDOR_INFO_ANDROID_METERED = "ANDROID_METERED"; /** + * DHCP Optional Type: Option overload option + */ + protected static final byte DHCP_OPTION_OVERLOAD = 52; + + /** + * Possible values of the option overload option. + */ + private static final byte OPTION_OVERLOAD_FILE = 1; + private static final byte OPTION_OVERLOAD_SNAME = 2; + private static final byte OPTION_OVERLOAD_BOTH = 3; + + /** * DHCP Optional Type: DHCP Requested IP Address */ protected static final byte DHCP_REQUESTED_IP = 50; @@ -314,6 +326,11 @@ public abstract class DhcpPacket { protected final byte[] mClientMac; /** + * The server host name from server. + */ + protected String mServerHostName; + + /** * Whether the packet should be built with rapid commit option */ protected boolean mRapidCommit; @@ -874,6 +891,8 @@ public abstract class DhcpPacket { Inet4Address ipDst = null; Inet4Address bcAddr = null; Inet4Address requestedIp = null; + String serverHostName; + byte optionOverload = 0; // The following are all unsigned integers. Internally we store them as signed integers of // the same length because that way we're guaranteed that they can't be out of the range of @@ -1015,9 +1034,9 @@ public abstract class DhcpPacket { packet.get(clientMac); // skip over address padding (16 octets allocated) - packet.position(packet.position() + (16 - addrLen) - + 64 // skip server host name (64 chars) - + 128); // skip boot file name (128 chars) + packet.position(packet.position() + (16 - addrLen)); + serverHostName = readAsciiString(packet, 64, false); + packet.position(packet.position() + 128); // Ensure this is a DHCP packet with a magic cookie, and not BOOTP. http://b/31850211 if (packet.remaining() < 4) { @@ -1128,6 +1147,11 @@ public abstract class DhcpPacket { // Embedded nulls are safe as this does not get passed to netd. vendorInfo = readAsciiString(packet, optionLen, true); break; + case DHCP_OPTION_OVERLOAD: + expectedLen = 1; + optionOverload = packet.get(); + optionOverload &= OPTION_OVERLOAD_BOTH; + break; default: // ignore any other parameters for (int i = 0; i < optionLen; i++) { @@ -1218,6 +1242,11 @@ public abstract class DhcpPacket { newPacket.mT2 = T2; newPacket.mVendorId = vendorId; newPacket.mVendorInfo = vendorInfo; + if ((optionOverload & OPTION_OVERLOAD_SNAME) == 0) { + newPacket.mServerHostName = serverHostName; + } else { + newPacket.mServerHostName = ""; + } return newPacket; } @@ -1277,6 +1306,7 @@ public abstract class DhcpPacket { results.vendorInfo = mVendorInfo; results.leaseDuration = (mLeaseTime != null) ? mLeaseTime : INFINITE_LEASE; results.mtu = (mMtu != null && MIN_MTU <= mMtu && mMtu <= MAX_MTU) ? mMtu : 0; + results.serverHostName = mServerHostName; return results; } diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java index d21b5f7853bb..b8ab94ce3830 100644 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java +++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java @@ -647,4 +647,9 @@ public class DhcpServer extends IDhcpServer.Stub { } } } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } } diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java index 06e1f05a40e6..e48b065409be 100644 --- a/packages/NetworkStack/src/android/net/ip/IpClient.java +++ b/packages/NetworkStack/src/android/net/ip/IpClient.java @@ -29,6 +29,7 @@ import android.net.INetd; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.NattKeepalivePacketDataParcelable; import android.net.NetworkStackIpMemoryStore; import android.net.ProvisioningConfigurationParcelable; import android.net.ProxyInfo; @@ -372,6 +373,10 @@ public class IpClient extends StateMachine { private boolean mMulticastFiltering; private long mStartTimeMillis; + /* This must match the definition in KeepaliveTracker.KeepaliveInfo */ + private static final int TYPE_NATT = 1; + private static final int TYPE_TCP = 2; + /** * Reading the snapshot is an asynchronous operation initiated by invoking * Callback.startReadPacketFilter() and completed when the WiFi Service responds with an @@ -554,10 +559,20 @@ public class IpClient extends StateMachine { IpClient.this.addKeepalivePacketFilter(slot, pkt); } @Override + public void addNattKeepalivePacketFilter(int slot, NattKeepalivePacketDataParcelable pkt) { + checkNetworkStackCallingPermission(); + IpClient.this.addNattKeepalivePacketFilter(slot, pkt); + } + @Override public void removeKeepalivePacketFilter(int slot) { checkNetworkStackCallingPermission(); IpClient.this.removeKeepalivePacketFilter(slot); } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } } public String getInterfaceName() { @@ -687,11 +702,20 @@ public class IpClient extends StateMachine { } /** - * Called by WifiStateMachine to add keepalive packet filter before setting up + * Called by WifiStateMachine to add TCP keepalive packet filter before setting up * keepalive offload. */ public void addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt) { - sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */, pkt); + sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, TYPE_TCP, pkt); + } + + /** + * Called by WifiStateMachine to add NATT keepalive packet filter before setting up + * keepalive offload. + */ + public void addNattKeepalivePacketFilter(int slot, + @NonNull NattKeepalivePacketDataParcelable pkt) { + sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, TYPE_NATT, pkt); } /** @@ -1613,9 +1637,16 @@ public class IpClient extends StateMachine { case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF: { final int slot = msg.arg1; + final int type = msg.arg2; + if (mApfFilter != null) { - mApfFilter.addKeepalivePacketFilter(slot, - (TcpKeepalivePacketDataParcelable) msg.obj); + if (type == TYPE_NATT) { + mApfFilter.addNattKeepalivePacketFilter(slot, + (NattKeepalivePacketDataParcelable) msg.obj); + } else { + mApfFilter.addTcpKeepalivePacketFilter(slot, + (TcpKeepalivePacketDataParcelable) msg.obj); + } } break; } diff --git a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java index 822678717092..2934e1cf0c82 100644 --- a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java +++ b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java @@ -35,6 +35,77 @@ public class NetworkStackUtils { // TODO: Refer to DeviceConfig definition. public static final String NAMESPACE_CONNECTIVITY = "connectivity"; + /** + * A list of captive portal detection specifications used in addition to the fallback URLs. + * Each spec has the format url@@/@@statusCodeRegex@@/@@contentRegex. Specs are separated + * by "@@,@@". + */ + public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = + "captive_portal_fallback_probe_specs"; + + /** + * A comma separated list of URLs used for captive portal detection in addition to the + * fallback HTTP url associated with the CAPTIVE_PORTAL_FALLBACK_URL settings. + */ + public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS = + "captive_portal_other_fallback_urls"; + + /** + * Which User-Agent string to use in the header of the captive portal detection probes. + * The User-Agent field is unset when this setting has no value (HttpUrlConnection default). + */ + public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent"; + + /** + * Whether to use HTTPS for network validation. This is enabled by default and the setting + * needs to be set to 0 to disable it. This setting is a misnomer because captive portals + * don't actually use HTTPS, but it's consistent with the other settings. + */ + public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https"; + + /** + * The URL used for HTTPS captive portal detection upon a new connection. + * A 204 response code from the server is used for validation. + */ + public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url"; + + /** + * The URL used for HTTP captive portal detection upon a new connection. + * A 204 response code from the server is used for validation. + */ + public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url"; + + /** + * The URL used for fallback HTTP captive portal detection when previous HTTP + * and HTTPS captive portal detection attemps did not return a conclusive answer. + */ + public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url"; + + /** + * What to do when connecting a network that presents a captive portal. + * Must be one of the CAPTIVE_PORTAL_MODE_* constants above. + * + * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT. + */ + public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode"; + + /** + * Don't attempt to detect captive portals. + */ + public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; + + /** + * When detecting a captive portal, display a notification that + * prompts the user to sign in. + */ + public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; + + /** + * When detecting a captive portal, immediately disconnect from the + * network and do not reconnect to that network in the future. + */ + public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; + static { System.loadLibrary("networkstackutilsjni"); } diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java index a0a90fde518f..a6d74842f631 100644 --- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java +++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java @@ -251,6 +251,11 @@ public class NetworkStackService extends Service { } } } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } } private static class NetworkMonitorImpl extends INetworkMonitor.Stub { @@ -325,5 +330,10 @@ public class NetworkStackService extends Service { checkNetworkStackCallingPermission(); mNm.notifyNetworkCapabilitiesChanged(nc); } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } } } diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java index 27d420328017..bacec78e5699 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java @@ -43,6 +43,16 @@ import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_EVALUATION_TYPE import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS; import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS; import static android.net.util.DataStallUtils.DEFAULT_DNS_LOG_SIZE; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_URL; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_HTTPS_URL; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_HTTP_URL; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE_IGNORE; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE_PROMPT; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USER_AGENT; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS; import static android.net.util.NetworkStackUtils.NAMESPACE_CONNECTIVITY; import static android.net.util.NetworkStackUtils.isEmpty; @@ -55,6 +65,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.net.ConnectivityManager; +import android.net.DnsResolver; import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; import android.net.LinkProperties; @@ -118,6 +129,7 @@ import java.util.Random; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; /** @@ -132,8 +144,13 @@ public class NetworkMonitor extends StateMachine { + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/60.0.3112.32 Safari/537.36"; + @VisibleForTesting + static final String CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT = + "captive_portal_dns_probe_timeout"; + private static final int SOCKET_TIMEOUT_MS = 10000; private static final int PROBE_TIMEOUT_MS = 3000; + enum EvaluationResult { VALIDATED(true), CAPTIVE_PORTAL(false); @@ -1164,20 +1181,47 @@ public class NetworkMonitor extends StateMachine { } private boolean getIsCaptivePortalCheckEnabled() { - String symbol = Settings.Global.CAPTIVE_PORTAL_MODE; - int defaultValue = Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT; + String symbol = CAPTIVE_PORTAL_MODE; + int defaultValue = CAPTIVE_PORTAL_MODE_PROMPT; int mode = mDependencies.getSetting(mContext, symbol, defaultValue); - return mode != Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE; + return mode != CAPTIVE_PORTAL_MODE_IGNORE; } private boolean getUseHttpsValidation() { - return mDependencies.getSetting(mContext, Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1; + return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, + CAPTIVE_PORTAL_USE_HTTPS, 1) == 1; } private String getCaptivePortalServerHttpsUrl() { return getSettingFromResource(mContext, R.string.config_captive_portal_https_url, - R.string.default_captive_portal_https_url, - Settings.Global.CAPTIVE_PORTAL_HTTPS_URL); + R.string.default_captive_portal_https_url, CAPTIVE_PORTAL_HTTPS_URL); + } + + private int getDnsProbeTimeout() { + return getIntSetting(mContext, R.integer.config_captive_portal_dns_probe_timeout, + CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, + R.integer.default_captive_portal_dns_probe_timeout); + } + + /** + * Gets an integer setting from resources or device config + * + * configResource is used if set, followed by device config if set, followed by defaultResource. + * If none of these are set then an exception is thrown. + * + * TODO: move to a common location such as a ConfigUtils class. + * TODO(b/130324939): test that the resources can be overlayed by an RRO package. + */ + @VisibleForTesting + int getIntSetting(@NonNull final Context context, @StringRes int configResource, + @NonNull String symbol, @StringRes int defaultResource) { + final Resources res = context.getResources(); + try { + return res.getInteger(configResource); + } catch (Resources.NotFoundException e) { + return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, + symbol, res.getInteger(defaultResource)); + } } /** @@ -1189,8 +1233,7 @@ public class NetworkMonitor extends StateMachine { */ public String getCaptivePortalServerHttpUrl() { return getSettingFromResource(mContext, R.string.config_captive_portal_http_url, - R.string.default_captive_portal_http_url, - Settings.Global.CAPTIVE_PORTAL_HTTP_URL); + R.string.default_captive_portal_http_url, CAPTIVE_PORTAL_HTTP_URL); } private int getConsecutiveDnsTimeoutThreshold() { @@ -1219,13 +1262,13 @@ public class NetworkMonitor extends StateMachine { private URL[] makeCaptivePortalFallbackUrls() { try { - final String firstUrl = mDependencies.getSetting(mContext, - Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, null); + final String firstUrl = mDependencies.getSetting(mContext, CAPTIVE_PORTAL_FALLBACK_URL, + null); final URL[] settingProviderUrls; if (!TextUtils.isEmpty(firstUrl)) { - final String otherUrls = mDependencies.getSetting(mContext, - Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, ""); + final String otherUrls = mDependencies.getDeviceConfigProperty( + NAMESPACE_CONNECTIVITY, CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, ""); // otherUrls may be empty, but .split() ignores trailing empty strings final String separator = ","; final String[] urls = (firstUrl + separator + otherUrls).split(separator); @@ -1245,8 +1288,9 @@ public class NetworkMonitor extends StateMachine { private CaptivePortalProbeSpec[] makeCaptivePortalFallbackProbeSpecs() { try { - final String settingsValue = mDependencies.getSetting( - mContext, Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS, null); + final String settingsValue = mDependencies.getDeviceConfigProperty( + NAMESPACE_CONNECTIVITY, CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS, null); + final CaptivePortalProbeSpec[] emptySpecs = new CaptivePortalProbeSpec[0]; final CaptivePortalProbeSpec[] providerValue = TextUtils.isEmpty(settingsValue) ? emptySpecs @@ -1340,8 +1384,8 @@ public class NetworkMonitor extends StateMachine { } private String getCaptivePortalUserAgent() { - return mDependencies.getSetting(mContext, - Settings.Global.CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT); + return mDependencies.getDeviceConfigProperty(NAMESPACE_CONNECTIVITY, + CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT); } private URL nextFallbackUrl() { @@ -1440,6 +1484,45 @@ public class NetworkMonitor extends StateMachine { return sendHttpProbe(url, probeType, null); } + /** Do a DNS lookup for the given server, or throw UnknownHostException after timeoutMs */ + @VisibleForTesting + protected InetAddress[] sendDnsProbeWithTimeout(String host, int timeoutMs) + throws UnknownHostException { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference<List<InetAddress>> resultRef = new AtomicReference<>(); + final DnsResolver.Callback<List<InetAddress>> callback = + new DnsResolver.Callback<List<InetAddress>>() { + public void onAnswer(List<InetAddress> answer, int rcode) { + if (rcode == 0) { + resultRef.set(answer); + } + latch.countDown(); + } + public void onError(@NonNull DnsResolver.DnsException e) { + validationLog("DNS error resolving " + host + ": " + e.getMessage()); + latch.countDown(); + } + }; + + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_PROBE); + mDependencies.getDnsResolver().query(mNetwork, host, DnsResolver.FLAG_EMPTY, + r -> r.run() /* executor */, null /* cancellationSignal */, callback); + TrafficStats.setThreadStatsTag(oldTag); + + try { + latch.await(timeoutMs, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + } + + List<InetAddress> result = resultRef.get(); + if (result == null || result.size() == 0) { + throw new UnknownHostException(host); + } + + return result.toArray(new InetAddress[0]); + } + /** Do a DNS resolution of the given server. */ private void sendDnsProbe(String host) { if (TextUtils.isEmpty(host)) { @@ -1451,7 +1534,7 @@ public class NetworkMonitor extends StateMachine { int result; String connectInfo; try { - InetAddress[] addresses = mNetwork.getAllByName(host); + InetAddress[] addresses = sendDnsProbeWithTimeout(host, getDnsProbeTimeout()); StringBuffer buffer = new StringBuffer(); for (InetAddress address : addresses) { buffer.append(',').append(address.getHostAddress()); @@ -1776,6 +1859,10 @@ public class NetworkMonitor extends StateMachine { return new OneAddressPerFamilyNetwork(network); } + public DnsResolver getDnsResolver() { + return DnsResolver.getInstance(); + } + public Random getRandom() { return new Random(); } diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java index bee4bbd9f42d..6a6bf83bd3c8 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java @@ -494,4 +494,9 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub { listener.onComplete(makeStatus(ERROR_INTERNAL_INTERRUPTED)); return true; } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } } diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java index 2775fde4c8b9..bea7052d8af2 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java @@ -91,6 +91,11 @@ public final class RegularMaintenanceJobService extends JobService { } @Override + public int getInterfaceVersion() { + return this.VERSION; + } + + @Override public IBinder asBinder() { return null; } diff --git a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java index a0e508f130a5..93ab3be28fc7 100644 --- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java +++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java @@ -1553,7 +1553,7 @@ public class ApfTest { parcel.seq = seqNum; parcel.ack = ackNum; - apfFilter.addKeepalivePacketFilter(slot1, parcel); + apfFilter.addTcpKeepalivePacketFilter(slot1, parcel); program = cb.getApfProgram(); // Verify IPv4 keepalive ack packet is dropped @@ -1592,7 +1592,7 @@ public class ApfTest { ipv6Parcel.seq = seqNum; ipv6Parcel.ack = ackNum; - apfFilter.addKeepalivePacketFilter(slot1, ipv6Parcel); + apfFilter.addTcpKeepalivePacketFilter(slot1, ipv6Parcel); program = cb.getApfProgram(); // Verify IPv6 keepalive ack packet is dropped @@ -1614,8 +1614,8 @@ public class ApfTest { apfFilter.removeKeepalivePacketFilter(slot1); // Verify multiple filters - apfFilter.addKeepalivePacketFilter(slot1, parcel); - apfFilter.addKeepalivePacketFilter(slot2, ipv6Parcel); + apfFilter.addTcpKeepalivePacketFilter(slot1, parcel); + apfFilter.addTcpKeepalivePacketFilter(slot2, ipv6Parcel); program = cb.getApfProgram(); // Verify IPv4 keepalive ack packet is dropped diff --git a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpPacketTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpPacketTest.java index 4d98403bfd4e..a30d3e492406 100644 --- a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpPacketTest.java +++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpPacketTest.java @@ -302,8 +302,9 @@ public class DhcpPacketTest { } private void assertDhcpResults(String ipAddress, String gateway, String dnsServersString, - String domains, String serverAddress, String vendorInfo, int leaseDuration, - boolean hasMeteredHint, int mtu, DhcpResults dhcpResults) throws Exception { + String domains, String serverAddress, String serverHostName, String vendorInfo, + int leaseDuration, boolean hasMeteredHint, int mtu, DhcpResults dhcpResults) + throws Exception { assertEquals(new LinkAddress(ipAddress), dhcpResults.ipAddress); assertEquals(v4Address(gateway), dhcpResults.gateway); @@ -316,6 +317,7 @@ public class DhcpPacketTest { assertEquals(domains, dhcpResults.domains); assertEquals(v4Address(serverAddress), dhcpResults.serverAddress); + assertEquals(serverHostName, dhcpResults.serverHostName); assertEquals(vendorInfo, dhcpResults.vendorInfo); assertEquals(leaseDuration, dhcpResults.leaseDuration); assertEquals(hasMeteredHint, dhcpResults.hasMeteredHint()); @@ -327,6 +329,7 @@ public class DhcpPacketTest { // TODO: Turn all of these into golden files. This will probably require using // androidx.test.InstrumentationRegistry for obtaining a Context object // to read such golden files, along with an appropriate Android.mk. + // CHECKSTYLE:OFF Generated code final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( // IP header. "451001480000000080118849c0a89003c0a89ff7" + @@ -347,16 +350,18 @@ public class DhcpPacketTest { // Options "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" + "3a0400000e103b040000189cff00000000000000000000")); + // CHECKSTYLE:ON Generated code DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3); assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null. DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4", - null, "192.168.144.3", null, 7200, false, 0, dhcpResults); + null, "192.168.144.3", "", null, 7200, false, 0, dhcpResults); } @Test public void testOffer2() throws Exception { + // CHECKSTYLE:OFF Generated code final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( // IP header. "450001518d0600004011144dc0a82b01c0a82bf7" + @@ -366,9 +371,9 @@ public class DhcpPacketTest { "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" + // MAC address. "30766ff2a90c00000000000000000000" + - // Server name. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + + // Server name ("dhcp.android.com" plus invalid "AAAA" after null terminator). + "646863702e616e64726f69642e636f6d00000000000000000000000000000000" + + "0000000000004141414100000000000000000000000000000000000000000000" + // File. "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + @@ -377,13 +382,15 @@ public class DhcpPacketTest { // Options "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" + "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff")); + // CHECKSTYLE:ON Generated code assertEquals(337, packet.limit()); DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3); assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null. DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertDhcpResults("192.168.43.247/24", "192.168.43.1", "192.168.43.1", - null, "192.168.43.1", "ANDROID_METERED", 3600, true, 0, dhcpResults); + null, "192.168.43.1", "dhcp.android.com", "ANDROID_METERED", 3600, true, 0, + dhcpResults); assertTrue(dhcpResults.hasMeteredHint()); } @@ -588,11 +595,12 @@ public class DhcpPacketTest { assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null. DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4", - null, "192.168.144.3", null, 7200, false, expectedMtu, dhcpResults); + null, "192.168.144.3", "", null, 7200, false, expectedMtu, dhcpResults); } @Test public void testMtu() throws Exception { + // CHECKSTYLE:OFF Generated code final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( // IP header. "451001480000000080118849c0a89003c0a89ff7" + @@ -613,6 +621,7 @@ public class DhcpPacketTest { // Options "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" + "3a0400000e103b040000189cff00000000")); + // CHECKSTYLE:ON Generated code checkMtu(packet, 0, null); checkMtu(packet, 0, mtuBytes(1501)); @@ -629,6 +638,7 @@ public class DhcpPacketTest { @Test public void testBadHwaddrLength() throws Exception { + // CHECKSTYLE:OFF Generated code final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( // IP header. "450001518d0600004011144dc0a82b01c0a82bf7" + @@ -649,6 +659,7 @@ public class DhcpPacketTest { // Options "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" + "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff")); + // CHECKSTYLE:ON Generated code String expectedClientMac = "30766FF2A90C"; final int hwAddrLenOffset = 20 + 8 + 2; @@ -705,6 +716,7 @@ public class DhcpPacketTest { // store any information in the overloaded fields). // // For now, we just check that it parses correctly. + // CHECKSTYLE:OFF Generated code final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( // Ethernet header. "b4cef6000000e80462236e300800" + @@ -727,16 +739,18 @@ public class DhcpPacketTest { // Options "638253633501023604010101010104ffff000033040000a8c03401030304ac1101010604ac110101" + "0000000000000000000000000000000000000000000000ff000000")); + // CHECKSTYLE:ON Generated code DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2); assertTrue(offerPacket instanceof DhcpOfferPacket); DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertDhcpResults("172.17.152.118/16", "172.17.1.1", "172.17.1.1", - null, "1.1.1.1", null, 43200, false, 0, dhcpResults); + null, "1.1.1.1", "", null, 43200, false, 0, dhcpResults); } @Test public void testBug2111() throws Exception { + // CHECKSTYLE:OFF Generated code final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( // IP header. "4500014c00000000ff119beac3eaf3880a3f5d04" + @@ -757,16 +771,18 @@ public class DhcpPacketTest { // Options. "638253633501023604c00002fe33040000bfc60104fffff00003040a3f50010608c0000201c0000202" + "0f0f646f6d61696e3132332e636f2e756b0000000000ff00000000")); + // CHECKSTYLE:ON Generated code DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3); assertTrue(offerPacket instanceof DhcpOfferPacket); DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertDhcpResults("10.63.93.4/20", "10.63.80.1", "192.0.2.1,192.0.2.2", - "domain123.co.uk", "192.0.2.254", null, 49094, false, 0, dhcpResults); + "domain123.co.uk", "192.0.2.254", "", null, 49094, false, 0, dhcpResults); } @Test public void testBug2136() throws Exception { + // CHECKSTYLE:OFF Generated code final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( // Ethernet header. "bcf5ac000000d0c7890000000800" + @@ -789,17 +805,19 @@ public class DhcpPacketTest { // Options. "6382536335010236040a20ff80330400001c200104fffff00003040a20900106089458413494584135" + "0f0b6c616e63732e61632e756b000000000000000000ff00000000")); + // CHECKSTYLE:ON Generated code DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2); assertTrue(offerPacket instanceof DhcpOfferPacket); assertEquals("BCF5AC000000", HexDump.toHexString(offerPacket.getClientMac())); DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53", - "lancs.ac.uk", "10.32.255.128", null, 7200, false, 0, dhcpResults); + "lancs.ac.uk", "10.32.255.128", "", null, 7200, false, 0, dhcpResults); } @Test public void testUdpServerAnySourcePort() throws Exception { + // CHECKSTYLE:OFF Generated code final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( // Ethernet header. "9cd917000000001c2e0000000800" + @@ -823,6 +841,7 @@ public class DhcpPacketTest { // Options. "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" + "d18180060f0777766d2e6564751c040a0fffffff000000")); + // CHECKSTYLE:ON Generated code DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2); assertTrue(offerPacket instanceof DhcpOfferPacket); @@ -830,11 +849,12 @@ public class DhcpPacketTest { DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertDhcpResults("10.15.122.242/16", "10.15.200.23", "209.129.128.3,209.129.148.3,209.129.128.6", - "wvm.edu", "10.1.105.252", null, 86400, false, 0, dhcpResults); + "wvm.edu", "10.1.105.252", "", null, 86400, false, 0, dhcpResults); } @Test public void testUdpInvalidDstPort() throws Exception { + // CHECKSTYLE:OFF Generated code final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( // Ethernet header. "9cd917000000001c2e0000000800" + @@ -858,6 +878,7 @@ public class DhcpPacketTest { // Options. "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" + "d18180060f0777766d2e6564751c040a0fffffff000000")); + // CHECKSTYLE:ON Generated code try { DhcpPacket.decodeFullPacket(packet, ENCAP_L2); @@ -867,6 +888,7 @@ public class DhcpPacketTest { @Test public void testMultipleRouters() throws Exception { + // CHECKSTYLE:OFF Generated code final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( // Ethernet header. "fc3d93000000" + "081735000000" + "0800" + @@ -889,13 +911,14 @@ public class DhcpPacketTest { // Options. "638253633501023604c0abbd023304000070803a04000038403b04000062700104ffffff00" + "0308c0a8bd01ffffff0006080808080808080404ff000000000000")); + // CHECKSTYLE:ON Generated code DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2); assertTrue(offerPacket instanceof DhcpOfferPacket); assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac())); DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4", - null, "192.171.189.2", null, 28800, false, 0, dhcpResults); + null, "192.171.189.2", "", null, 28800, false, 0, dhcpResults); } @Test diff --git a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java index 7d5e9e3ba174..f0e2f1b8d459 100644 --- a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java +++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java @@ -133,6 +133,11 @@ public class DhcpServerTest { public void onStatusAvailable(int statusCode) { assertEquals(STATUS_SUCCESS, statusCode); } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; @Before diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java index 910bdc7e600f..0dc1cbf8a984 100644 --- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java +++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java @@ -26,10 +26,14 @@ import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_EVALUATION_TYPE; import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL; import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD; import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -38,6 +42,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; @@ -52,6 +57,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.net.ConnectivityManager; +import android.net.DnsResolver; import android.net.INetworkMonitorCallbacks; import android.net.InetAddresses; import android.net.LinkProperties; @@ -66,6 +72,7 @@ import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.ConditionVariable; import android.os.Handler; +import android.os.Looper; import android.os.RemoteException; import android.os.SystemClock; import android.provider.Settings; @@ -76,6 +83,7 @@ import android.util.ArrayMap; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.networkstack.R; import com.android.networkstack.metrics.DataStallDetectionStats; import com.android.networkstack.metrics.DataStallStatsUtils; @@ -93,8 +101,12 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.URL; +import java.net.UnknownHostException; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Random; +import java.util.concurrent.Executor; import javax.net.ssl.SSLHandshakeException; @@ -108,6 +120,7 @@ public class NetworkMonitorTest { private @Mock IpConnectivityLog mLogger; private @Mock SharedLog mValidationLogger; private @Mock NetworkInfo mNetworkInfo; + private @Mock DnsResolver mDnsResolver; private @Mock ConnectivityManager mCm; private @Mock TelephonyManager mTelephony; private @Mock WifiManager mWifi; @@ -153,14 +166,40 @@ public class NetworkMonitorTest { private static final NetworkCapabilities NO_INTERNET_CAPABILITIES = new NetworkCapabilities() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + private void setDnsAnswers(String[] answers) throws UnknownHostException { + if (answers == null) { + doThrow(new UnknownHostException()).when(mNetwork).getAllByName(any()); + doNothing().when(mDnsResolver).query(any(), any(), anyInt(), any(), any(), any()); + return; + } + + List<InetAddress> answerList = new ArrayList<>(); + for (String answer : answers) { + answerList.add(InetAddresses.parseNumericAddress(answer)); + } + InetAddress[] answerArray = answerList.toArray(new InetAddress[0]); + + doReturn(answerArray).when(mNetwork).getAllByName(any()); + + doAnswer((invocation) -> { + Executor executor = (Executor) invocation.getArgument(3); + DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(5); + new Handler(Looper.getMainLooper()).post(() -> { + executor.execute(() -> callback.onAnswer(answerList, 0)); + }); + return null; + }).when(mDnsResolver).query(eq(mNetwork), any(), anyInt(), any(), any(), any()); + } + @Before public void setUp() throws IOException { MockitoAnnotations.initMocks(this); when(mDependencies.getPrivateDnsBypassNetwork(any())).thenReturn(mNetwork); + when(mDependencies.getDnsResolver()).thenReturn(mDnsResolver); when(mDependencies.getRandom()).thenReturn(mRandom); when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())) .thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); - when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_USE_HTTPS), + when(mDependencies.getDeviceConfigPropertyInt(any(), eq(CAPTIVE_PORTAL_USE_HTTPS), anyInt())).thenReturn(1); when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any())) .thenReturn(TEST_HTTP_URL); @@ -201,9 +240,8 @@ public class NetworkMonitorTest { }).when(mNetwork).openConnection(any()); when(mHttpConnection.getRequestProperties()).thenReturn(new ArrayMap<>()); when(mHttpsConnection.getRequestProperties()).thenReturn(new ArrayMap<>()); - doReturn(new InetAddress[] { - InetAddresses.parseNumericAddress("192.168.0.0") - }).when(mNetwork).getAllByName(any()); + + setDnsAnswers(new String[]{"2001:db8::1", "192.0.2.2"}); when(mContext.registerReceiver(any(BroadcastReceiver.class), any())).then((invocation) -> { mRegisteredReceivers.add(invocation.getArgument(0)); @@ -310,6 +348,44 @@ public class NetworkMonitorTest { } @Test + public void testGetIntSetting() throws Exception { + WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); + + // No config resource, no device config. Expect to get default resource. + doThrow(new Resources.NotFoundException()) + .when(mResources).getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout)); + doAnswer(invocation -> { + int defaultValue = invocation.getArgument(2); + return defaultValue; + }).when(mDependencies).getDeviceConfigPropertyInt(any(), + eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT), + anyInt()); + when(mResources.getInteger(eq(R.integer.default_captive_portal_dns_probe_timeout))) + .thenReturn(42); + assertEquals(42, wnm.getIntSetting(mContext, + R.integer.config_captive_portal_dns_probe_timeout, + NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, + R.integer.default_captive_portal_dns_probe_timeout)); + + // Set device config. Expect to get device config. + when(mDependencies.getDeviceConfigPropertyInt(any(), + eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT), anyInt())) + .thenReturn(1234); + assertEquals(1234, wnm.getIntSetting(mContext, + R.integer.config_captive_portal_dns_probe_timeout, + NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, + R.integer.default_captive_portal_dns_probe_timeout)); + + // Set config resource. Expect to get config resource. + when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout))) + .thenReturn(5678); + assertEquals(5678, wnm.getIntSetting(mContext, + R.integer.config_captive_portal_dns_probe_timeout, + NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, + R.integer.default_captive_portal_dns_probe_timeout)); + } + + @Test public void testIsCaptivePortal_HttpProbeIsPortal() throws IOException { setSslException(mHttpsConnection); setPortal302(mHttpConnection); @@ -639,6 +715,45 @@ public class NetworkMonitorTest { runPartialConnectivityNetworkTest(); } + private void assertIpAddressArrayEquals(String[] expected, InetAddress[] actual) { + String[] actualStrings = new String[actual.length]; + for (int i = 0; i < actual.length; i++) { + actualStrings[i] = actual[i].getHostAddress(); + } + assertArrayEquals("Array of IP addresses differs", expected, actualStrings); + } + + @Test + public void testSendDnsProbeWithTimeout() throws Exception { + WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); + final int shortTimeoutMs = 200; + + String[] expected = new String[]{"2001:db8::"}; + setDnsAnswers(expected); + InetAddress[] actual = wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); + assertIpAddressArrayEquals(expected, actual); + + expected = new String[]{"2001:db8::", "192.0.2.1"}; + setDnsAnswers(expected); + actual = wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); + assertIpAddressArrayEquals(expected, actual); + + expected = new String[0]; + setDnsAnswers(expected); + try { + wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); + fail("No DNS results, expected UnknownHostException"); + } catch (UnknownHostException e) { + } + + setDnsAnswers(null); + try { + wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); + fail("DNS query timed out, expected UnknownHostException"); + } catch (UnknownHostException e) { + } + } + private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) { for (int i = 0; i < count; i++) { wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount( @@ -683,13 +798,13 @@ public class NetworkMonitorTest { } private void setOtherFallbackUrls(String urls) { - when(mDependencies.getSetting(any(), - eq(Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS), any())).thenReturn(urls); + when(mDependencies.getDeviceConfigProperty(any(), + eq(CAPTIVE_PORTAL_OTHER_FALLBACK_URLS), any())).thenReturn(urls); } private void setFallbackSpecs(String specs) { - when(mDependencies.getSetting(any(), - eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs); + when(mDependencies.getDeviceConfigProperty(any(), + eq(CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs); } private void setCaptivePortalMode(int mode) { diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java index a00eff7992d4..87346e5d6a28 100644 --- a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java +++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java @@ -136,6 +136,11 @@ public class IpMemoryStoreServiceTest { public IBinder asBinder() { return null; } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; } @@ -156,6 +161,11 @@ public class IpMemoryStoreServiceTest { public IBinder asBinder() { return null; } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; } @@ -178,6 +188,11 @@ public class IpMemoryStoreServiceTest { public IBinder asBinder() { return null; } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; } @@ -200,6 +215,11 @@ public class IpMemoryStoreServiceTest { public IBinder asBinder() { return null; } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; } @@ -219,6 +239,11 @@ public class IpMemoryStoreServiceTest { public IBinder asBinder() { return null; } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; } |