1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.connectivity; 18 19 import static android.net.CaptivePortal.APP_RETURN_DISMISSED; 20 import static android.net.DnsResolver.TYPE_A; 21 import static android.net.DnsResolver.TYPE_AAAA; 22 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS; 23 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK; 24 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP; 25 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS; 26 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS; 27 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; 28 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; 29 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 30 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; 31 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; 32 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; 33 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 34 import static android.net.NetworkCapabilities.TRANSPORT_VPN; 35 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 36 import static android.net.metrics.ValidationProbeEvent.PROBE_HTTP; 37 import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD; 38 import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_EVALUATION_TYPE; 39 import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL; 40 import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_TCP_POLLING_INTERVAL; 41 import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD; 42 import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS; 43 import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_TCP; 44 import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_EVALUATION_TYPES; 45 import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS; 46 import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS; 47 import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS; 48 import static android.net.util.NetworkStackUtils.DEFAULT_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT; 49 import static android.net.util.NetworkStackUtils.DISMISS_PORTAL_IN_VALIDATED_NETWORK; 50 import static android.net.util.NetworkStackUtils.DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION; 51 import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL; 52 import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL; 53 import static android.net.util.NetworkStackUtils.TEST_URL_EXPIRATION_TIME; 54 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; 55 56 import static com.android.networkstack.util.DnsUtils.PRIVATE_DNS_PROBE_HOST_SUFFIX; 57 import static com.android.server.connectivity.NetworkMonitor.INITIAL_REEVALUATE_DELAY_MS; 58 import static com.android.server.connectivity.NetworkMonitor.extractCharset; 59 60 import static junit.framework.Assert.assertEquals; 61 import static junit.framework.Assert.assertFalse; 62 63 import static org.junit.Assert.assertArrayEquals; 64 import static org.junit.Assert.assertNotEquals; 65 import static org.junit.Assert.assertNotNull; 66 import static org.junit.Assert.assertNull; 67 import static org.junit.Assert.assertTrue; 68 import static org.junit.Assert.fail; 69 import static org.junit.Assume.assumeFalse; 70 import static org.junit.Assume.assumeTrue; 71 import static org.mockito.ArgumentMatchers.anyBoolean; 72 import static org.mockito.ArgumentMatchers.argThat; 73 import static org.mockito.ArgumentMatchers.eq; 74 import static org.mockito.Mockito.after; 75 import static org.mockito.Mockito.any; 76 import static org.mockito.Mockito.anyInt; 77 import static org.mockito.Mockito.atLeastOnce; 78 import static org.mockito.Mockito.atMost; 79 import static org.mockito.Mockito.doAnswer; 80 import static org.mockito.Mockito.doReturn; 81 import static org.mockito.Mockito.doThrow; 82 import static org.mockito.Mockito.mock; 83 import static org.mockito.Mockito.never; 84 import static org.mockito.Mockito.reset; 85 import static org.mockito.Mockito.spy; 86 import static org.mockito.Mockito.timeout; 87 import static org.mockito.Mockito.times; 88 import static org.mockito.Mockito.verify; 89 import static org.mockito.Mockito.when; 90 91 import static java.lang.System.currentTimeMillis; 92 import static java.util.Collections.singletonList; 93 import static java.util.stream.Collectors.toList; 94 95 import android.annotation.NonNull; 96 import android.annotation.SuppressLint; 97 import android.content.BroadcastReceiver; 98 import android.content.Context; 99 import android.content.ContextWrapper; 100 import android.content.Intent; 101 import android.content.pm.PackageManager; 102 import android.content.res.Configuration; 103 import android.content.res.Resources; 104 import android.net.CaptivePortalData; 105 import android.net.ConnectivityManager; 106 import android.net.DataStallReportParcelable; 107 import android.net.DnsResolver; 108 import android.net.INetd; 109 import android.net.INetworkMonitorCallbacks; 110 import android.net.InetAddresses; 111 import android.net.LinkProperties; 112 import android.net.Network; 113 import android.net.NetworkCapabilities; 114 import android.net.NetworkTestResultParcelable; 115 import android.net.Uri; 116 import android.net.captiveportal.CaptivePortalProbeResult; 117 import android.net.metrics.IpConnectivityLog; 118 import android.net.shared.NetworkMonitorUtils; 119 import android.net.shared.PrivateDnsConfig; 120 import android.net.util.SharedLog; 121 import android.net.wifi.WifiInfo; 122 import android.net.wifi.WifiManager; 123 import android.os.Build; 124 import android.os.Bundle; 125 import android.os.ConditionVariable; 126 import android.os.Handler; 127 import android.os.IBinder; 128 import android.os.Looper; 129 import android.os.Process; 130 import android.os.RemoteException; 131 import android.os.SystemClock; 132 import android.provider.Settings; 133 import android.telephony.CellIdentityGsm; 134 import android.telephony.CellIdentityLte; 135 import android.telephony.CellInfo; 136 import android.telephony.CellInfoGsm; 137 import android.telephony.CellInfoLte; 138 import android.telephony.CellSignalStrength; 139 import android.telephony.TelephonyManager; 140 import android.util.ArrayMap; 141 142 import androidx.test.filters.SmallTest; 143 import androidx.test.runner.AndroidJUnit4; 144 145 import com.android.networkstack.NetworkStackNotifier; 146 import com.android.networkstack.R; 147 import com.android.networkstack.apishim.CaptivePortalDataShimImpl; 148 import com.android.networkstack.apishim.ConstantsShim; 149 import com.android.networkstack.apishim.NetworkInformationShimImpl; 150 import com.android.networkstack.apishim.common.CaptivePortalDataShim; 151 import com.android.networkstack.apishim.common.NetworkInformationShim; 152 import com.android.networkstack.apishim.common.ShimUtils; 153 import com.android.networkstack.apishim.common.UnsupportedApiLevelException; 154 import com.android.networkstack.metrics.DataStallDetectionStats; 155 import com.android.networkstack.metrics.DataStallStatsUtils; 156 import com.android.networkstack.netlink.TcpSocketTracker; 157 import com.android.server.NetworkStackService.NetworkStackServiceManager; 158 import com.android.server.connectivity.nano.CellularData; 159 import com.android.server.connectivity.nano.DnsEvent; 160 import com.android.server.connectivity.nano.WifiData; 161 import com.android.testutils.DevSdkIgnoreRule; 162 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; 163 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; 164 import com.android.testutils.HandlerUtils; 165 166 import com.google.protobuf.nano.MessageNano; 167 168 import junit.framework.AssertionFailedError; 169 170 import org.junit.After; 171 import org.junit.Before; 172 import org.junit.Rule; 173 import org.junit.Test; 174 import org.junit.runner.RunWith; 175 import org.mockito.ArgumentCaptor; 176 import org.mockito.Mock; 177 import org.mockito.MockitoAnnotations; 178 import org.mockito.Spy; 179 import org.mockito.invocation.InvocationOnMock; 180 import org.mockito.stubbing.Answer; 181 182 import java.io.ByteArrayInputStream; 183 import java.io.IOException; 184 import java.io.InputStream; 185 import java.lang.reflect.Constructor; 186 import java.net.HttpURLConnection; 187 import java.net.Inet6Address; 188 import java.net.InetAddress; 189 import java.net.URL; 190 import java.net.UnknownHostException; 191 import java.nio.charset.Charset; 192 import java.nio.charset.StandardCharsets; 193 import java.util.ArrayList; 194 import java.util.Arrays; 195 import java.util.Collections; 196 import java.util.HashMap; 197 import java.util.HashSet; 198 import java.util.List; 199 import java.util.Map; 200 import java.util.Objects; 201 import java.util.Random; 202 import java.util.concurrent.Executor; 203 import java.util.concurrent.TimeUnit; 204 205 import javax.net.ssl.SSLHandshakeException; 206 207 @RunWith(AndroidJUnit4.class) 208 @SmallTest 209 @SuppressLint("NewApi") // Uses hidden APIs, which the linter would identify as missing APIs. 210 public class NetworkMonitorTest { 211 private static final String LOCATION_HEADER = "location"; 212 private static final String CONTENT_TYPE_HEADER = "Content-Type"; 213 214 @Rule 215 public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); 216 217 private @Mock Context mContext; 218 private @Mock Configuration mConfiguration; 219 private @Mock Resources mResources; 220 private @Mock IpConnectivityLog mLogger; 221 private @Mock SharedLog mValidationLogger; 222 private @Mock DnsResolver mDnsResolver; 223 private @Mock ConnectivityManager mCm; 224 private @Mock TelephonyManager mTelephony; 225 private @Mock WifiManager mWifi; 226 private @Mock NetworkStackServiceManager mServiceManager; 227 private @Mock NetworkStackNotifier mNotifier; 228 private @Mock HttpURLConnection mHttpConnection; 229 private @Mock HttpURLConnection mOtherHttpConnection1; 230 private @Mock HttpURLConnection mOtherHttpConnection2; 231 private @Mock HttpURLConnection mHttpsConnection; 232 private @Mock HttpURLConnection mOtherHttpsConnection1; 233 private @Mock HttpURLConnection mOtherHttpsConnection2; 234 private @Mock HttpURLConnection mFallbackConnection; 235 private @Mock HttpURLConnection mOtherFallbackConnection; 236 private @Mock HttpURLConnection mTestOverriddenUrlConnection; 237 private @Mock HttpURLConnection mCapportApiConnection; 238 private @Mock HttpURLConnection mSpeedTestConnection; 239 private @Mock Random mRandom; 240 private @Mock NetworkMonitor.Dependencies mDependencies; 241 // Mockito can't create a mock of INetworkMonitorCallbacks on Q because it can't find 242 // CaptivePortalData on notifyCaptivePortalDataChanged. Use a spy on a mock IBinder instead. 243 private INetworkMonitorCallbacks mCallbacks = spy( 244 INetworkMonitorCallbacks.Stub.asInterface(mock(IBinder.class))); 245 private @Spy Network mCleartextDnsNetwork = new Network(TEST_NETID); 246 private @Mock Network mNetwork; 247 private @Mock DataStallStatsUtils mDataStallStatsUtils; 248 private @Mock TcpSocketTracker.Dependencies mTstDependencies; 249 private @Mock INetd mNetd; 250 private @Mock TcpSocketTracker mTst; 251 private HashSet<WrappedNetworkMonitor> mCreatedNetworkMonitors; 252 private HashSet<BroadcastReceiver> mRegisteredReceivers; 253 private @Mock Context mMccContext; 254 private @Mock Resources mMccResource; 255 private @Mock WifiInfo mWifiInfo; 256 257 private static final int TEST_NETID = 4242; 258 private static final String TEST_HTTP_URL = "http://www.google.com/gen_204"; 259 private static final String TEST_HTTP_OTHER_URL1 = "http://other1.google.com/gen_204"; 260 private static final String TEST_HTTP_OTHER_URL2 = "http://other2.google.com/gen_204"; 261 private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204"; 262 private static final String TEST_HTTPS_OTHER_URL1 = "https://other1.google.com/gen_204"; 263 private static final String TEST_HTTPS_OTHER_URL2 = "https://other2.google.com/gen_204"; 264 private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204"; 265 private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204"; 266 private static final String TEST_INVALID_OVERRIDE_URL = "https://override.example.com/test"; 267 private static final String TEST_OVERRIDE_URL = "http://localhost:12345/test"; 268 private static final String TEST_CAPPORT_API_URL = "https://capport.example.com/api"; 269 private static final String TEST_LOGIN_URL = "https://testportal.example.com/login"; 270 private static final String TEST_VENUE_INFO_URL = "https://venue.example.com/info"; 271 private static final String TEST_SPEED_TEST_URL = "https://speedtest.example.com"; 272 private static final String TEST_RELATIVE_URL = "/test/relative/gen_204"; 273 private static final String TEST_MCCMNC = "123456"; 274 private static final String TEST_FRIENDLY_NAME = "Friendly Name"; 275 private static final String[] TEST_HTTP_URLS = {TEST_HTTP_OTHER_URL1, TEST_HTTP_OTHER_URL2}; 276 private static final String[] TEST_HTTPS_URLS = {TEST_HTTPS_OTHER_URL1, TEST_HTTPS_OTHER_URL2}; 277 private static final int TEST_TCP_FAIL_RATE = 99; 278 private static final int TEST_TCP_PACKET_COUNT = 50; 279 private static final long TEST_ELAPSED_TIME_MS = 123456789L; 280 private static final int TEST_SIGNAL_STRENGTH = -100; 281 private static final int VALIDATION_RESULT_INVALID = 0; 282 private static final int VALIDATION_RESULT_PORTAL = 0; 283 private static final String TEST_REDIRECT_URL = "android.com"; 284 private static final int PROBES_PRIVDNS_VALID = NETWORK_VALIDATION_PROBE_DNS 285 | NETWORK_VALIDATION_PROBE_HTTPS | NETWORK_VALIDATION_PROBE_PRIVDNS; 286 287 private static final int RETURN_CODE_DNS_SUCCESS = 0; 288 private static final int RETURN_CODE_DNS_TIMEOUT = 255; 289 private static final int DEFAULT_DNS_TIMEOUT_THRESHOLD = 5; 290 291 private static final int HANDLER_TIMEOUT_MS = 1000; 292 private static final int TEST_MIN_STALL_EVALUATE_INTERVAL_MS = 500; 293 private static final int STALL_EXPECTED_LAST_PROBE_TIME_MS = 294 TEST_MIN_STALL_EVALUATE_INTERVAL_MS + HANDLER_TIMEOUT_MS; 295 private static final LinkProperties TEST_LINK_PROPERTIES = new LinkProperties(); 296 297 // Cannot have a static member for the LinkProperties with captive portal API information, as 298 // the initializer would crash on Q (the members in LinkProperties were introduced in R). makeCapportLPs()299 private static LinkProperties makeCapportLPs() { 300 final LinkProperties lp = new LinkProperties(TEST_LINK_PROPERTIES); 301 lp.setCaptivePortalApiUrl(Uri.parse(TEST_CAPPORT_API_URL)); 302 return lp; 303 } 304 305 private static final NetworkCapabilities CELL_METERED_CAPABILITIES = new NetworkCapabilities() 306 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) 307 .addCapability(NET_CAPABILITY_INTERNET); 308 309 private static final NetworkCapabilities CELL_NOT_METERED_CAPABILITIES = 310 new NetworkCapabilities() 311 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) 312 .addCapability(NET_CAPABILITY_INTERNET) 313 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); 314 315 private static final NetworkCapabilities WIFI_NOT_METERED_CAPABILITIES = 316 new NetworkCapabilities() 317 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) 318 .addCapability(NET_CAPABILITY_INTERNET) 319 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); 320 321 private static final NetworkCapabilities CELL_NO_INTERNET_CAPABILITIES = 322 new NetworkCapabilities().addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); 323 324 private static final NetworkCapabilities WIFI_OEM_PAID_CAPABILITIES = 325 new NetworkCapabilities() 326 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) 327 .addCapability(NET_CAPABILITY_INTERNET) 328 .addCapability(NET_CAPABILITY_NOT_METERED) 329 .addCapability(NET_CAPABILITY_OEM_PAID) 330 .removeCapability(NET_CAPABILITY_NOT_RESTRICTED); 331 332 /** 333 * Fakes DNS responses. 334 * 335 * Allows test methods to configure the IP addresses that will be resolved by 336 * Network#getAllByName and by DnsResolver#query. 337 */ 338 class FakeDns { 339 /** Data class to record the Dns entry. */ 340 class DnsEntry { 341 final String mHostname; 342 final int mType; 343 final List<InetAddress> mAddresses; DnsEntry(String host, int type, List<InetAddress> addr)344 DnsEntry(String host, int type, List<InetAddress> addr) { 345 mHostname = host; 346 mType = type; 347 mAddresses = addr; 348 } 349 // Full match or partial match that target host contains the entry hostname to support 350 // random private dns probe hostname. matches(String hostname, int type)351 private boolean matches(String hostname, int type) { 352 return hostname.endsWith(mHostname) && type == mType; 353 } 354 } 355 private final ArrayList<DnsEntry> mAnswers = new ArrayList<DnsEntry>(); 356 private boolean mNonBypassPrivateDnsWorking = true; 357 358 /** Whether DNS queries on mNonBypassPrivateDnsWorking should succeed. */ setNonBypassPrivateDnsWorking(boolean working)359 private void setNonBypassPrivateDnsWorking(boolean working) { 360 mNonBypassPrivateDnsWorking = working; 361 } 362 363 /** Clears all DNS entries. */ clearAll()364 private synchronized void clearAll() { 365 mAnswers.clear(); 366 } 367 368 /** Returns the answer for a given name and type on the given mock network. */ getAnswer(Object mock, String hostname, int type)369 private synchronized List<InetAddress> getAnswer(Object mock, String hostname, int type) { 370 if (mock == mNetwork && !mNonBypassPrivateDnsWorking) { 371 return null; 372 } 373 374 return mAnswers.stream().filter(e -> e.matches(hostname, type)) 375 .map(answer -> answer.mAddresses).findFirst().orElse(null); 376 } 377 378 /** Sets the answer for a given name and type. */ setAnswer(String hostname, String[] answer, int type)379 private synchronized void setAnswer(String hostname, String[] answer, int type) 380 throws UnknownHostException { 381 DnsEntry record = new DnsEntry(hostname, type, generateAnswer(answer)); 382 // Remove the existing one. 383 mAnswers.removeIf(entry -> entry.matches(hostname, type)); 384 // Add or replace a new record. 385 mAnswers.add(record); 386 } 387 generateAnswer(String[] answer)388 private List<InetAddress> generateAnswer(String[] answer) { 389 if (answer == null) return new ArrayList<>(); 390 return Arrays.stream(answer).map(addr -> InetAddress.parseNumericAddress(addr)) 391 .collect(toList()); 392 } 393 394 /** Simulates a getAllByName call for the specified name on the specified mock network. */ getAllByName(Object mock, String hostname)395 private InetAddress[] getAllByName(Object mock, String hostname) 396 throws UnknownHostException { 397 List<InetAddress> answer = queryAllTypes(mock, hostname); 398 if (answer == null || answer.size() == 0) { 399 throw new UnknownHostException(hostname); 400 } 401 return answer.toArray(new InetAddress[0]); 402 } 403 404 // Regardless of the type, depends on what the responses contained in the network. queryAllTypes(Object mock, String hostname)405 private List<InetAddress> queryAllTypes(Object mock, String hostname) { 406 List<InetAddress> answer = new ArrayList<>(); 407 addAllIfNotNull(answer, getAnswer(mock, hostname, TYPE_A)); 408 addAllIfNotNull(answer, getAnswer(mock, hostname, TYPE_AAAA)); 409 return answer; 410 } 411 addAllIfNotNull(List<InetAddress> list, List<InetAddress> c)412 private void addAllIfNotNull(List<InetAddress> list, List<InetAddress> c) { 413 if (c != null) { 414 list.addAll(c); 415 } 416 } 417 418 /** Starts mocking DNS queries. */ startMocking()419 private void startMocking() throws UnknownHostException { 420 // Queries on mNetwork using getAllByName. 421 doAnswer(invocation -> { 422 return getAllByName(invocation.getMock(), invocation.getArgument(0)); 423 }).when(mNetwork).getAllByName(any()); 424 425 // Queries on mCleartextDnsNetwork using DnsResolver#query. 426 doAnswer(invocation -> { 427 return mockQuery(invocation, 1 /* posHostname */, 3 /* posExecutor */, 428 5 /* posCallback */, -1 /* posType */); 429 }).when(mDnsResolver).query(any(), any(), anyInt(), any(), any(), any()); 430 431 // Queries on mCleartextDnsNetwork using DnsResolver#query with QueryType. 432 doAnswer(invocation -> { 433 return mockQuery(invocation, 1 /* posHostname */, 4 /* posExecutor */, 434 6 /* posCallback */, 2 /* posType */); 435 }).when(mDnsResolver).query(any(), any(), anyInt(), anyInt(), any(), any(), any()); 436 } 437 438 // Mocking queries on DnsResolver#query. mockQuery(InvocationOnMock invocation, int posHostname, int posExecutor, int posCallback, int posType)439 private Answer mockQuery(InvocationOnMock invocation, int posHostname, int posExecutor, 440 int posCallback, int posType) { 441 String hostname = (String) invocation.getArgument(posHostname); 442 Executor executor = (Executor) invocation.getArgument(posExecutor); 443 DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(posCallback); 444 List<InetAddress> answer; 445 446 answer = posType != -1 447 ? getAnswer(invocation.getMock(), hostname, invocation.getArgument(posType)) : 448 queryAllTypes(invocation.getMock(), hostname); 449 450 if (answer != null && answer.size() > 0) { 451 new Handler(Looper.getMainLooper()).post(() -> { 452 executor.execute(() -> callback.onAnswer(answer, 0)); 453 }); 454 } 455 // If no answers, do nothing. sendDnsProbeWithTimeout will time out and throw UHE. 456 return null; 457 } 458 } 459 460 private FakeDns mFakeDns; 461 462 @Before setUp()463 public void setUp() throws Exception { 464 MockitoAnnotations.initMocks(this); 465 when(mDependencies.getPrivateDnsBypassNetwork(any())).thenReturn(mCleartextDnsNetwork); 466 when(mDependencies.getDnsResolver()).thenReturn(mDnsResolver); 467 when(mDependencies.getRandom()).thenReturn(mRandom); 468 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())) 469 .thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); 470 when(mDependencies.getDeviceConfigPropertyInt(any(), eq(CAPTIVE_PORTAL_USE_HTTPS), 471 anyInt())).thenReturn(1); 472 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any())) 473 .thenReturn(TEST_HTTP_URL); 474 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL), any())) 475 .thenReturn(TEST_HTTPS_URL); 476 477 doReturn(mCleartextDnsNetwork).when(mNetwork).getPrivateDnsBypassingCopy(); 478 479 when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mCm); 480 when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephony); 481 when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifi); 482 when(mContext.getResources()).thenReturn(mResources); 483 484 when(mServiceManager.getNotifier()).thenReturn(mNotifier); 485 486 when(mTelephony.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE); 487 when(mTelephony.getNetworkOperator()).thenReturn(TEST_MCCMNC); 488 when(mTelephony.getSimOperator()).thenReturn(TEST_MCCMNC); 489 490 when(mResources.getString(anyInt())).thenReturn(""); 491 when(mResources.getStringArray(anyInt())).thenReturn(new String[0]); 492 doReturn(mConfiguration).when(mResources).getConfiguration(); 493 when(mMccContext.getResources()).thenReturn(mMccResource); 494 495 setFallbackUrl(TEST_FALLBACK_URL); 496 setOtherFallbackUrls(TEST_OTHER_FALLBACK_URL); 497 setFallbackSpecs(null); // Test with no fallback spec by default 498 when(mRandom.nextInt()).thenReturn(0); 499 500 when(mTstDependencies.getNetd()).thenReturn(mNetd); 501 // DNS probe timeout should not be defined more than half of HANDLER_TIMEOUT_MS. Otherwise, 502 // it will fail the test because of timeout expired for querying AAAA and A sequentially. 503 when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout))) 504 .thenReturn(200); 505 506 doAnswer((invocation) -> { 507 URL url = invocation.getArgument(0); 508 switch(url.toString()) { 509 case TEST_HTTP_URL: 510 return mHttpConnection; 511 case TEST_HTTP_OTHER_URL1: 512 return mOtherHttpConnection1; 513 case TEST_HTTP_OTHER_URL2: 514 return mOtherHttpConnection2; 515 case TEST_HTTPS_URL: 516 return mHttpsConnection; 517 case TEST_HTTPS_OTHER_URL1: 518 return mOtherHttpsConnection1; 519 case TEST_HTTPS_OTHER_URL2: 520 return mOtherHttpsConnection2; 521 case TEST_FALLBACK_URL: 522 return mFallbackConnection; 523 case TEST_OTHER_FALLBACK_URL: 524 return mOtherFallbackConnection; 525 case TEST_OVERRIDE_URL: 526 case TEST_INVALID_OVERRIDE_URL: 527 return mTestOverriddenUrlConnection; 528 case TEST_CAPPORT_API_URL: 529 return mCapportApiConnection; 530 case TEST_SPEED_TEST_URL: 531 return mSpeedTestConnection; 532 default: 533 fail("URL not mocked: " + url.toString()); 534 return null; 535 } 536 }).when(mCleartextDnsNetwork).openConnection(any()); 537 when(mHttpConnection.getRequestProperties()).thenReturn(new ArrayMap<>()); 538 when(mHttpsConnection.getRequestProperties()).thenReturn(new ArrayMap<>()); 539 540 mFakeDns = new FakeDns(); 541 mFakeDns.startMocking(); 542 // Set private dns suffix answer. sendPrivateDnsProbe() in NetworkMonitor send probe with 543 // one time hostname. The hostname will be [random generated UUID] + HOST_SUFFIX differently 544 // each time. That means the host answer cannot be pre-set into the answer list. Thus, set 545 // the host suffix and use partial match in FakeDns to match the target host and reply the 546 // intended answer. 547 mFakeDns.setAnswer(PRIVATE_DNS_PROBE_HOST_SUFFIX, new String[]{"192.0.2.2"}, TYPE_A); 548 mFakeDns.setAnswer(PRIVATE_DNS_PROBE_HOST_SUFFIX, new String[]{"2001:db8::1"}, TYPE_AAAA); 549 550 when(mContext.registerReceiver(any(BroadcastReceiver.class), any())).then((invocation) -> { 551 mRegisteredReceivers.add(invocation.getArgument(0)); 552 return new Intent(); 553 }); 554 555 doAnswer((invocation) -> { 556 mRegisteredReceivers.remove(invocation.getArgument(0)); 557 return null; 558 }).when(mContext).unregisterReceiver(any()); 559 560 resetCallbacks(); 561 562 setMinDataStallEvaluateInterval(TEST_MIN_STALL_EVALUATE_INTERVAL_MS); 563 setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS); 564 setValidDataStallDnsTimeThreshold(500); 565 setConsecutiveDnsTimeoutThreshold(5); 566 mCreatedNetworkMonitors = new HashSet<>(); 567 mRegisteredReceivers = new HashSet<>(); 568 setDismissPortalInValidatedNetwork(false); 569 } 570 571 @After tearDown()572 public void tearDown() { 573 mFakeDns.clearAll(); 574 // Make a local copy of mCreatedNetworkMonitors because during the iteration below, 575 // WrappedNetworkMonitor#onQuitting will delete elements from it on the handler threads. 576 WrappedNetworkMonitor[] networkMonitors = mCreatedNetworkMonitors.toArray( 577 new WrappedNetworkMonitor[0]); 578 for (WrappedNetworkMonitor nm : networkMonitors) { 579 nm.notifyNetworkDisconnected(); 580 nm.awaitQuit(); 581 } 582 assertEquals("NetworkMonitor still running after disconnect", 583 0, mCreatedNetworkMonitors.size()); 584 assertEquals("BroadcastReceiver still registered after disconnect", 585 0, mRegisteredReceivers.size()); 586 } 587 resetCallbacks()588 private void resetCallbacks() { 589 resetCallbacks(6); 590 } 591 resetCallbacks(int interfaceVersion)592 private void resetCallbacks(int interfaceVersion) { 593 reset(mCallbacks); 594 try { 595 doReturn(interfaceVersion).when(mCallbacks).getInterfaceVersion(); 596 } catch (RemoteException e) { 597 // Can't happen as mCallbacks is a mock 598 fail("Error mocking getInterfaceVersion" + e); 599 } 600 } 601 getTcpSocketTrackerOrNull(NetworkMonitor.Dependencies dp)602 private TcpSocketTracker getTcpSocketTrackerOrNull(NetworkMonitor.Dependencies dp) { 603 return ((dp.getDeviceConfigPropertyInt( 604 NAMESPACE_CONNECTIVITY, 605 CONFIG_DATA_STALL_EVALUATION_TYPE, 606 DEFAULT_DATA_STALL_EVALUATION_TYPES) 607 & DATA_STALL_EVALUATION_TYPE_TCP) != 0) ? mTst : null; 608 } 609 610 private class WrappedNetworkMonitor extends NetworkMonitor { 611 private long mProbeTime = 0; 612 private final ConditionVariable mQuitCv = new ConditionVariable(false); 613 WrappedNetworkMonitor()614 WrappedNetworkMonitor() { 615 super(mContext, mCallbacks, mNetwork, mLogger, mValidationLogger, mServiceManager, 616 mDependencies, getTcpSocketTrackerOrNull(mDependencies)); 617 } 618 619 @Override getLastProbeTime()620 protected long getLastProbeTime() { 621 return mProbeTime; 622 } 623 setLastProbeTime(long time)624 protected void setLastProbeTime(long time) { 625 mProbeTime = time; 626 } 627 628 @Override addDnsEvents(@onNull final DataStallDetectionStats.Builder stats)629 protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) { 630 if ((getDataStallEvaluationType() & DATA_STALL_EVALUATION_TYPE_DNS) != 0) { 631 generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD); 632 } 633 } 634 635 @Override onQuitting()636 protected void onQuitting() { 637 assertTrue(mCreatedNetworkMonitors.remove(this)); 638 mQuitCv.open(); 639 } 640 awaitQuit()641 protected void awaitQuit() { 642 assertTrue("NetworkMonitor did not quit after " + HANDLER_TIMEOUT_MS + "ms", 643 mQuitCv.block(HANDLER_TIMEOUT_MS)); 644 } 645 getContext()646 protected Context getContext() { 647 return mContext; 648 } 649 } 650 makeMonitor(NetworkCapabilities nc)651 private WrappedNetworkMonitor makeMonitor(NetworkCapabilities nc) { 652 final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(); 653 nm.start(); 654 setNetworkCapabilities(nm, nc); 655 HandlerUtils.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS); 656 mCreatedNetworkMonitors.add(nm); 657 when(mTstDependencies.isTcpInfoParsingSupported()).thenReturn(false); 658 659 return nm; 660 } 661 makeCellMeteredNetworkMonitor()662 private WrappedNetworkMonitor makeCellMeteredNetworkMonitor() { 663 final WrappedNetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES); 664 return nm; 665 } 666 makeCellNotMeteredNetworkMonitor()667 private WrappedNetworkMonitor makeCellNotMeteredNetworkMonitor() { 668 final WrappedNetworkMonitor nm = makeMonitor(CELL_NOT_METERED_CAPABILITIES); 669 return nm; 670 } 671 makeWifiNotMeteredNetworkMonitor()672 private WrappedNetworkMonitor makeWifiNotMeteredNetworkMonitor() { 673 final WrappedNetworkMonitor nm = makeMonitor(WIFI_NOT_METERED_CAPABILITIES); 674 return nm; 675 } 676 setNetworkCapabilities(NetworkMonitor nm, NetworkCapabilities nc)677 private void setNetworkCapabilities(NetworkMonitor nm, NetworkCapabilities nc) { 678 nm.notifyNetworkCapabilitiesChanged(nc); 679 HandlerUtils.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS); 680 } 681 682 @Test testOnlyWifiTransport()683 public void testOnlyWifiTransport() { 684 final WrappedNetworkMonitor wnm = makeMonitor(CELL_METERED_CAPABILITIES); 685 assertFalse(wnm.onlyWifiTransport()); 686 final NetworkCapabilities nc = new NetworkCapabilities() 687 .addTransportType(TRANSPORT_WIFI) 688 .addTransportType(TRANSPORT_VPN); 689 setNetworkCapabilities(wnm, nc); 690 assertFalse(wnm.onlyWifiTransport()); 691 nc.removeTransportType(TRANSPORT_VPN); 692 setNetworkCapabilities(wnm, nc); 693 assertTrue(wnm.onlyWifiTransport()); 694 } 695 696 @Test testNeedEvaluatingBandwidth()697 public void testNeedEvaluatingBandwidth() throws Exception { 698 // Make metered network first, the transport type is TRANSPORT_CELLULAR. That means the 699 // test cannot pass the condition check in needEvaluatingBandwidth(). 700 final WrappedNetworkMonitor wnm1 = makeCellMeteredNetworkMonitor(); 701 // Don't set the config_evaluating_bandwidth_url to make 702 // the condition check fail in needEvaluatingBandwidth(). 703 assertFalse(wnm1.needEvaluatingBandwidth()); 704 // Make the NetworkCapabilities to have the TRANSPORT_WIFI but it still cannot meet the 705 // condition check. 706 final NetworkCapabilities nc = new NetworkCapabilities() 707 .addTransportType(TRANSPORT_WIFI); 708 setNetworkCapabilities(wnm1, nc); 709 assertFalse(wnm1.needEvaluatingBandwidth()); 710 // Make the network to be non-metered wifi but it still cannot meet the condition check 711 // since the config_evaluating_bandwidth_url is not set. 712 nc.addCapability(NET_CAPABILITY_NOT_METERED); 713 setNetworkCapabilities(wnm1, nc); 714 assertFalse(wnm1.needEvaluatingBandwidth()); 715 // All configurations are set correctly. 716 doReturn(TEST_SPEED_TEST_URL).when(mResources).getString( 717 R.string.config_evaluating_bandwidth_url); 718 final WrappedNetworkMonitor wnm2 = makeCellMeteredNetworkMonitor(); 719 setNetworkCapabilities(wnm2, nc); 720 assertTrue(wnm2.needEvaluatingBandwidth()); 721 // Set mIsBandwidthCheckPassedOrIgnored to true and expect needEvaluatingBandwidth() will 722 // return false. 723 wnm2.mIsBandwidthCheckPassedOrIgnored = true; 724 assertFalse(wnm2.needEvaluatingBandwidth()); 725 // Reset mIsBandwidthCheckPassedOrIgnored back to false. 726 wnm2.mIsBandwidthCheckPassedOrIgnored = false; 727 // Shouldn't evaluate network bandwidth on the metered wifi. 728 nc.removeCapability(NET_CAPABILITY_NOT_METERED); 729 setNetworkCapabilities(wnm2, nc); 730 assertFalse(wnm2.needEvaluatingBandwidth()); 731 // Shouldn't evaluate network bandwidth on the unmetered cellular. 732 nc.addCapability(NET_CAPABILITY_NOT_METERED); 733 nc.removeTransportType(TRANSPORT_WIFI); 734 nc.addTransportType(TRANSPORT_CELLULAR); 735 assertFalse(wnm2.needEvaluatingBandwidth()); 736 } 737 738 @Test testEvaluatingBandwidthState_meteredNetwork()739 public void testEvaluatingBandwidthState_meteredNetwork() throws Exception { 740 setStatus(mHttpsConnection, 204); 741 setStatus(mHttpConnection, 204); 742 final NetworkCapabilities meteredCap = new NetworkCapabilities() 743 .addTransportType(TRANSPORT_WIFI) 744 .addCapability(NET_CAPABILITY_INTERNET); 745 doReturn(TEST_SPEED_TEST_URL).when(mResources).getString( 746 R.string.config_evaluating_bandwidth_url); 747 final NetworkMonitor nm = runNetworkTest(TEST_LINK_PROPERTIES, meteredCap, 748 NETWORK_VALIDATION_RESULT_VALID, NETWORK_VALIDATION_PROBE_DNS 749 | NETWORK_VALIDATION_PROBE_HTTPS, null /* redirectUrl */); 750 // Evaluating bandwidth process won't be executed when the network is metered wifi. 751 // Check that the connection hasn't been opened and the state should transition to validated 752 // state directly. 753 verify(mCleartextDnsNetwork, never()).openConnection(new URL(TEST_SPEED_TEST_URL)); 754 assertEquals(NETWORK_VALIDATION_RESULT_VALID, 755 nm.getEvaluationState().getEvaluationResult()); 756 } 757 758 @Test testEvaluatingBandwidthState_nonMeteredNetworkWithWrongConfig()759 public void testEvaluatingBandwidthState_nonMeteredNetworkWithWrongConfig() throws Exception { 760 setStatus(mHttpsConnection, 204); 761 setStatus(mHttpConnection, 204); 762 final NetworkCapabilities nonMeteredCap = new NetworkCapabilities() 763 .addTransportType(TRANSPORT_WIFI) 764 .addCapability(NET_CAPABILITY_INTERNET) 765 .addCapability(NET_CAPABILITY_NOT_METERED); 766 doReturn("").when(mResources).getString(R.string.config_evaluating_bandwidth_url); 767 final NetworkMonitor nm = runNetworkTest(TEST_LINK_PROPERTIES, nonMeteredCap, 768 NETWORK_VALIDATION_RESULT_VALID, NETWORK_VALIDATION_PROBE_DNS 769 | NETWORK_VALIDATION_PROBE_HTTPS, null /* redirectUrl */); 770 // Non-metered network with wrong configuration(the config_evaluating_bandwidth_url is 771 // empty). Check that the connection hasn't been opened and the state should transition to 772 // validated state directly. 773 verify(mCleartextDnsNetwork, never()).openConnection(new URL(TEST_SPEED_TEST_URL)); 774 assertEquals(NETWORK_VALIDATION_RESULT_VALID, 775 nm.getEvaluationState().getEvaluationResult()); 776 } 777 778 @Test testMatchesHttpContent()779 public void testMatchesHttpContent() throws Exception { 780 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); 781 doReturn("[\\s\\S]*line2[\\s\\S]*").when(mResources).getString( 782 R.string.config_network_validation_failed_content_regexp); 783 assertTrue(wnm.matchesHttpContent("This is line1\nThis is line2\nThis is line3", 784 R.string.config_network_validation_failed_content_regexp)); 785 assertFalse(wnm.matchesHttpContent("hello", 786 R.string.config_network_validation_failed_content_regexp)); 787 // Set an invalid regex and expect to get the false even though the regex is the same as the 788 // content. 789 doReturn("[").when(mResources).getString( 790 R.string.config_network_validation_failed_content_regexp); 791 assertFalse(wnm.matchesHttpContent("[", 792 R.string.config_network_validation_failed_content_regexp)); 793 } 794 795 @Test testMatchesHttpContentLength()796 public void testMatchesHttpContentLength() throws Exception { 797 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); 798 // Set the range of content length. 799 doReturn(100).when(mResources).getInteger(R.integer.config_min_matches_http_content_length); 800 doReturn(1000).when(mResources).getInteger( 801 R.integer.config_max_matches_http_content_length); 802 assertFalse(wnm.matchesHttpContentLength(100)); 803 assertFalse(wnm.matchesHttpContentLength(1000)); 804 assertTrue(wnm.matchesHttpContentLength(500)); 805 806 // Test the invalid value. 807 assertFalse(wnm.matchesHttpContentLength(-1)); 808 assertFalse(wnm.matchesHttpContentLength(0)); 809 assertFalse(wnm.matchesHttpContentLength(Integer.MAX_VALUE + 1L)); 810 811 // Set the wrong value for min and max config to make sure the function is working even 812 // though the config is wrong. 813 doReturn(1000).when(mResources).getInteger( 814 R.integer.config_min_matches_http_content_length); 815 doReturn(100).when(mResources).getInteger( 816 R.integer.config_max_matches_http_content_length); 817 assertFalse(wnm.matchesHttpContentLength(100)); 818 assertFalse(wnm.matchesHttpContentLength(1000)); 819 assertFalse(wnm.matchesHttpContentLength(500)); 820 } 821 822 @Test testGetResStringConfig()823 public void testGetResStringConfig() throws Exception { 824 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); 825 // Set the config and expect to get the customized value. 826 final String regExp = ".*HTTP.*200.*not a captive portal.*"; 827 doReturn(regExp).when(mResources).getString( 828 R.string.config_network_validation_failed_content_regexp); 829 assertEquals(regExp, wnm.getResStringConfig(mContext, 830 R.string.config_network_validation_failed_content_regexp, null)); 831 doThrow(new Resources.NotFoundException()).when(mResources).getString(eq( 832 R.string.config_network_validation_failed_content_regexp)); 833 // If the config is not found, then expect to get the default value - null. 834 assertNull(wnm.getResStringConfig(mContext, 835 R.string.config_network_validation_failed_content_regexp, null)); 836 } 837 838 @Test testGetResIntConfig()839 public void testGetResIntConfig() throws Exception { 840 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); 841 // Set the config and expect to get the customized value. 842 doReturn(100).when(mResources).getInteger(R.integer.config_min_matches_http_content_length); 843 doReturn(1000).when(mResources).getInteger( 844 R.integer.config_max_matches_http_content_length); 845 assertEquals(100, wnm.getResIntConfig(mContext, 846 R.integer.config_min_matches_http_content_length, Integer.MAX_VALUE)); 847 assertEquals(1000, wnm.getResIntConfig(mContext, 848 R.integer.config_max_matches_http_content_length, 0)); 849 doThrow(new Resources.NotFoundException()) 850 .when(mResources).getInteger( 851 eq(R.integer.config_min_matches_http_content_length)); 852 doThrow(new Resources.NotFoundException()) 853 .when(mResources).getInteger(eq(R.integer.config_max_matches_http_content_length)); 854 // If the config is not found, then expect to get the default value. 855 assertEquals(Integer.MAX_VALUE, wnm.getResIntConfig(mContext, 856 R.integer.config_min_matches_http_content_length, Integer.MAX_VALUE)); 857 assertEquals(0, wnm.getResIntConfig(mContext, 858 R.integer.config_max_matches_http_content_length, 0)); 859 } 860 861 @Test testGetHttpProbeUrl()862 public void testGetHttpProbeUrl() { 863 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); 864 // If config_captive_portal_http_url is set and the global setting is set, the config is 865 // used. 866 doReturn(TEST_HTTP_URL).when(mResources).getString(R.string.config_captive_portal_http_url); 867 doReturn(TEST_HTTP_OTHER_URL2).when(mResources).getString( 868 R.string.default_captive_portal_http_url); 869 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any())) 870 .thenReturn(TEST_HTTP_OTHER_URL1); 871 assertEquals(TEST_HTTP_URL, wnm.getCaptivePortalServerHttpUrl()); 872 // If config_captive_portal_http_url is unset and the global setting is set, the global 873 // setting is used. 874 doReturn(null).when(mResources).getString(R.string.config_captive_portal_http_url); 875 assertEquals(TEST_HTTP_OTHER_URL1, wnm.getCaptivePortalServerHttpUrl()); 876 // If both config_captive_portal_http_url and global setting are unset, 877 // default_captive_portal_http_url is used. 878 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any())) 879 .thenReturn(null); 880 assertEquals(TEST_HTTP_OTHER_URL2, wnm.getCaptivePortalServerHttpUrl()); 881 } 882 883 @Test testGetLocationMcc()884 public void testGetLocationMcc() throws Exception { 885 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); 886 doReturn(PackageManager.PERMISSION_DENIED).when(mContext).checkPermission( 887 eq(android.Manifest.permission.ACCESS_FINE_LOCATION), anyInt(), anyInt()); 888 assertNull(wnm.getLocationMcc()); 889 doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkPermission( 890 eq(android.Manifest.permission.ACCESS_FINE_LOCATION), anyInt(), anyInt()); 891 doReturn(new ContextWrapper(mContext)).when(mContext).createConfigurationContext(any()); 892 doReturn(null).when(mTelephony).getAllCellInfo(); 893 assertNull(wnm.getLocationMcc()); 894 // Prepare CellInfo and check if the vote mechanism is working or not. 895 final List<CellInfo> cellList = new ArrayList<CellInfo>(); 896 doReturn(cellList).when(mTelephony).getAllCellInfo(); 897 assertNull(wnm.getLocationMcc()); 898 cellList.add(makeTestCellInfoGsm("460")); 899 cellList.add(makeTestCellInfoGsm("460")); 900 cellList.add(makeTestCellInfoLte("466")); 901 // The count of 460 is 2 and the count of 466 is 1, so the getLocationMcc() should return 902 // 460. 903 assertEquals("460", wnm.getLocationMcc()); 904 // getCustomizedContextOrDefault() shouldn't return mContext when using neighbor mcc 905 // is enabled and the sim is not ready. 906 doReturn(true).when(mResources).getBoolean(R.bool.config_no_sim_card_uses_neighbor_mcc); 907 doReturn(TelephonyManager.SIM_STATE_ABSENT).when(mTelephony).getSimState(); 908 assertEquals(460, 909 wnm.getCustomizedContextOrDefault().getResources().getConfiguration().mcc); 910 doReturn(false).when(mResources).getBoolean(R.bool.config_no_sim_card_uses_neighbor_mcc); 911 assertEquals(wnm.getContext(), wnm.getCustomizedContextOrDefault()); 912 } 913 914 @Test testGetMccMncOverrideInfo()915 public void testGetMccMncOverrideInfo() { 916 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); 917 doReturn(new ContextWrapper(mContext)).when(mContext).createConfigurationContext(any()); 918 // 1839 is VZW's carrier id. 919 doReturn(1839).when(mTelephony).getSimCarrierId(); 920 assertNull(wnm.getMccMncOverrideInfo()); 921 // 1854 is CTC's carrier id. 922 doReturn(1854).when(mTelephony).getSimCarrierId(); 923 assertNotNull(wnm.getMccMncOverrideInfo()); 924 // Check if the mcc & mnc has changed as expected. 925 assertEquals(460, 926 wnm.getCustomizedContextOrDefault().getResources().getConfiguration().mcc); 927 assertEquals(03, 928 wnm.getCustomizedContextOrDefault().getResources().getConfiguration().mnc); 929 // Every mcc and mnc should be set in sCarrierIdToMccMnc. 930 // Check if there is any unset value in mcc or mnc. 931 for (int i = 0; i < wnm.sCarrierIdToMccMnc.size(); i++) { 932 assertNotEquals(-1, wnm.sCarrierIdToMccMnc.valueAt(i).mcc); 933 assertNotEquals(-1, wnm.sCarrierIdToMccMnc.valueAt(i).mnc); 934 } 935 } 936 makeTestCellInfoGsm(String mcc)937 private CellInfoGsm makeTestCellInfoGsm(String mcc) throws Exception { 938 final CellInfoGsm info = new CellInfoGsm(); 939 final CellIdentityGsm ci = makeCellIdentityGsm(0, 0, 0, 0, mcc, "01", "", ""); 940 info.setCellIdentity(ci); 941 return info; 942 } 943 makeTestCellInfoLte(String mcc)944 private CellInfoLte makeTestCellInfoLte(String mcc) throws Exception { 945 final CellInfoLte info = new CellInfoLte(); 946 final CellIdentityLte ci = makeCellIdentityLte(0, 0, 0, 0, 0, mcc, "01", "", ""); 947 info.setCellIdentity(ci); 948 return info; 949 } 950 setupNoSimCardNeighborMcc()951 private void setupNoSimCardNeighborMcc() throws Exception { 952 // Enable using neighbor resource by camping mcc feature. 953 doReturn(true).when(mResources).getBoolean(R.bool.config_no_sim_card_uses_neighbor_mcc); 954 final List<CellInfo> cellList = new ArrayList<CellInfo>(); 955 final int testMcc = 460; 956 cellList.add(makeTestCellInfoGsm(Integer.toString(testMcc))); 957 doReturn(cellList).when(mTelephony).getAllCellInfo(); 958 final Configuration config = mResources.getConfiguration(); 959 config.mcc = testMcc; 960 doReturn(mMccContext).when(mContext).createConfigurationContext(eq(config)); 961 } 962 963 @Test testMakeFallbackUrls()964 public void testMakeFallbackUrls() throws Exception { 965 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); 966 // Value exist in setting provider. 967 URL[] urls = wnm.makeCaptivePortalFallbackUrls(); 968 assertEquals(urls[0].toString(), TEST_FALLBACK_URL); 969 970 // Clear setting provider value. Verify it to get configuration from resource instead. 971 setFallbackUrl(null); 972 // Verify that getting resource with exception. 973 when(mResources.getStringArray(R.array.config_captive_portal_fallback_urls)) 974 .thenThrow(Resources.NotFoundException.class); 975 urls = wnm.makeCaptivePortalFallbackUrls(); 976 assertEquals(urls.length, 0); 977 978 // Verify resource return 2 different URLs. 979 doReturn(new String[] {"http://testUrl1.com", "http://testUrl2.com"}).when(mResources) 980 .getStringArray(R.array.config_captive_portal_fallback_urls); 981 urls = wnm.makeCaptivePortalFallbackUrls(); 982 assertEquals(urls.length, 2); 983 assertEquals("http://testUrl1.com", urls[0].toString()); 984 assertEquals("http://testUrl2.com", urls[1].toString()); 985 986 // Even though the using neighbor resource by camping mcc feature is enabled, the 987 // customized context has been assigned and won't change. So calling 988 // makeCaptivePortalFallbackUrls() still gets the original value. 989 setupNoSimCardNeighborMcc(); 990 doReturn(new String[] {"http://testUrl3.com"}).when(mMccResource) 991 .getStringArray(R.array.config_captive_portal_fallback_urls); 992 urls = wnm.makeCaptivePortalFallbackUrls(); 993 assertEquals(urls.length, 2); 994 assertEquals("http://testUrl1.com", urls[0].toString()); 995 assertEquals("http://testUrl2.com", urls[1].toString()); 996 } 997 998 @Test testMakeFallbackUrlsWithCustomizedContext()999 public void testMakeFallbackUrlsWithCustomizedContext() throws Exception { 1000 // Value is expected to be replaced by location resource. 1001 setupNoSimCardNeighborMcc(); 1002 doReturn(new String[] {"http://testUrl.com"}).when(mMccResource) 1003 .getStringArray(R.array.config_captive_portal_fallback_urls); 1004 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); 1005 final URL[] urls = wnm.makeCaptivePortalFallbackUrls(); 1006 assertEquals(urls.length, 1); 1007 assertEquals("http://testUrl.com", urls[0].toString()); 1008 } 1009 makeCellIdentityGsm(int lac, int cid, int arfcn, int bsic, String mccStr, String mncStr, String alphal, String alphas)1010 private static CellIdentityGsm makeCellIdentityGsm(int lac, int cid, int arfcn, int bsic, 1011 String mccStr, String mncStr, String alphal, String alphas) 1012 throws ReflectiveOperationException { 1013 if (ShimUtils.isAtLeastR()) { 1014 return new CellIdentityGsm(lac, cid, arfcn, bsic, mccStr, mncStr, alphal, alphas, 1015 Collections.emptyList() /* additionalPlmns */); 1016 } else { 1017 // API <= Q does not have the additionalPlmns parameter 1018 final Constructor<CellIdentityGsm> constructor = CellIdentityGsm.class.getConstructor( 1019 int.class, int.class, int.class, int.class, String.class, String.class, 1020 String.class, String.class); 1021 return constructor.newInstance(lac, cid, arfcn, bsic, mccStr, mncStr, alphal, alphas); 1022 } 1023 } 1024 makeCellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, String mccStr, String mncStr, String alphal, String alphas)1025 private static CellIdentityLte makeCellIdentityLte(int ci, int pci, int tac, int earfcn, 1026 int bandwidth, String mccStr, String mncStr, String alphal, String alphas) 1027 throws ReflectiveOperationException { 1028 if (ShimUtils.isAtLeastR()) { 1029 return new CellIdentityLte(ci, pci, tac, earfcn, new int[] {} /* bands */, 1030 bandwidth, mccStr, mncStr, alphal, alphas, 1031 Collections.emptyList() /* additionalPlmns */, null /* csgInfo */); 1032 } else { 1033 // API <= Q does not have the additionalPlmns and csgInfo parameters 1034 final Constructor<CellIdentityLte> constructor = CellIdentityLte.class.getConstructor( 1035 int.class, int.class, int.class, int.class, int.class, String.class, 1036 String.class, String.class, String.class); 1037 return constructor.newInstance(ci, pci, tac, earfcn, bandwidth, mccStr, mncStr, alphal, 1038 alphas); 1039 } 1040 } 1041 1042 @Test testGetIntSetting()1043 public void testGetIntSetting() throws Exception { 1044 WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); 1045 1046 // No config resource, no device config. Expect to get default resource. 1047 doThrow(new Resources.NotFoundException()) 1048 .when(mResources).getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout)); 1049 doAnswer(invocation -> { 1050 int defaultValue = invocation.getArgument(2); 1051 return defaultValue; 1052 }).when(mDependencies).getDeviceConfigPropertyInt(any(), 1053 eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT), 1054 anyInt()); 1055 assertEquals(DEFAULT_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, wnm.getIntSetting(mContext, 1056 R.integer.config_captive_portal_dns_probe_timeout, 1057 NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, 1058 DEFAULT_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT)); 1059 1060 // Set device config. Expect to get device config. 1061 when(mDependencies.getDeviceConfigPropertyInt(any(), 1062 eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT), anyInt())) 1063 .thenReturn(1234); 1064 assertEquals(1234, wnm.getIntSetting(mContext, 1065 R.integer.config_captive_portal_dns_probe_timeout, 1066 NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, 1067 DEFAULT_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT)); 1068 1069 // Set config resource. Expect to get config resource. 1070 when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout))) 1071 .thenReturn(5678); 1072 assertEquals(5678, wnm.getIntSetting(mContext, 1073 R.integer.config_captive_portal_dns_probe_timeout, 1074 NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, 1075 DEFAULT_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT)); 1076 } 1077 1078 @Test testIsCaptivePortal_HttpProbeIsPortal()1079 public void testIsCaptivePortal_HttpProbeIsPortal() throws Exception { 1080 setSslException(mHttpsConnection); 1081 setPortal302(mHttpConnection); 1082 runPortalNetworkTest(); 1083 } 1084 1085 @Test testIsCaptivePortal_Http200EmptyResponse()1086 public void testIsCaptivePortal_Http200EmptyResponse() throws Exception { 1087 setSslException(mHttpsConnection); 1088 setStatus(mHttpConnection, 200); 1089 // Invalid if there is no content (can't login to an empty page) 1090 runNetworkTest(VALIDATION_RESULT_INVALID, 0 /* probesSucceeded */, null); 1091 verify(mCallbacks, never()).showProvisioningNotification(any(), any()); 1092 } 1093 doCaptivePortal200ResponseTest(String expectedRedirectUrl)1094 private void doCaptivePortal200ResponseTest(String expectedRedirectUrl) throws Exception { 1095 setSslException(mHttpsConnection); 1096 setStatus(mHttpConnection, 200); 1097 doReturn(100L).when(mHttpConnection).getContentLengthLong(); 1098 // Redirect URL was null before S 1099 runNetworkTest(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, expectedRedirectUrl); 1100 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)).showProvisioningNotification(any(), any()); 1101 } 1102 1103 @Test @IgnoreAfter(Build.VERSION_CODES.R) testIsCaptivePortal_HttpProbeIs200Portal_R()1104 public void testIsCaptivePortal_HttpProbeIs200Portal_R() throws Exception { 1105 doCaptivePortal200ResponseTest(null); 1106 } 1107 1108 @Test @IgnoreUpTo(Build.VERSION_CODES.R) testIsCaptivePortal_HttpProbeIs200Portal()1109 public void testIsCaptivePortal_HttpProbeIs200Portal() throws Exception { 1110 doCaptivePortal200ResponseTest(TEST_HTTP_URL); 1111 } 1112 setupPrivateIpResponse(String privateAddr)1113 private void setupPrivateIpResponse(String privateAddr) throws Exception { 1114 setSslException(mHttpsConnection); 1115 setPortal302(mHttpConnection); 1116 final String httpHost = new URL(TEST_HTTP_URL).getHost(); 1117 mFakeDns.setAnswer(httpHost, new String[] { "2001:db8::123" }, TYPE_AAAA); 1118 final InetAddress parsedPrivateAddr = InetAddresses.parseNumericAddress(privateAddr); 1119 mFakeDns.setAnswer(httpHost, new String[] { privateAddr }, 1120 (parsedPrivateAddr instanceof Inet6Address) ? TYPE_AAAA : TYPE_A); 1121 } 1122 1123 @Test testIsCaptivePortal_PrivateIpNotPortal_Enabled_IPv4()1124 public void testIsCaptivePortal_PrivateIpNotPortal_Enabled_IPv4() throws Exception { 1125 when(mDependencies.isFeatureEnabled(any(), eq(DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION))) 1126 .thenReturn(true); 1127 setupPrivateIpResponse("192.168.1.1"); 1128 runFailedNetworkTest(); 1129 } 1130 1131 @Test testIsCaptivePortal_PrivateIpNotPortal_Enabled_IPv6()1132 public void testIsCaptivePortal_PrivateIpNotPortal_Enabled_IPv6() throws Exception { 1133 when(mDependencies.isFeatureEnabled(any(), eq(DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION))) 1134 .thenReturn(true); 1135 setupPrivateIpResponse("fec0:1234::1"); 1136 runFailedNetworkTest(); 1137 } 1138 1139 @Test testIsCaptivePortal_PrivateIpNotPortal_Disabled()1140 public void testIsCaptivePortal_PrivateIpNotPortal_Disabled() throws Exception { 1141 setupPrivateIpResponse("192.168.1.1"); 1142 runPortalNetworkTest(); 1143 } 1144 1145 @Test testIsCaptivePortal_HttpsProbeIsNotPortal()1146 public void testIsCaptivePortal_HttpsProbeIsNotPortal() throws Exception { 1147 setStatus(mHttpsConnection, 204); 1148 setStatus(mHttpConnection, 500); 1149 1150 runValidatedNetworkTest(); 1151 } 1152 1153 @Test testIsCaptivePortal_FallbackProbeIsPortal()1154 public void testIsCaptivePortal_FallbackProbeIsPortal() throws Exception { 1155 setSslException(mHttpsConnection); 1156 setStatus(mHttpConnection, 500); 1157 setPortal302(mFallbackConnection); 1158 runPortalNetworkTest(); 1159 } 1160 1161 @Test testIsCaptivePortal_HttpSucceedFallbackProbeIsPortal()1162 public void testIsCaptivePortal_HttpSucceedFallbackProbeIsPortal() throws Exception { 1163 setSslException(mHttpsConnection); 1164 setStatus(mHttpConnection, 204); 1165 setPortal302(mFallbackConnection); 1166 runPortalNetworkTest(); 1167 } 1168 1169 @Test testIsCaptivePortal_FallbackProbeIsNotPortal()1170 public void testIsCaptivePortal_FallbackProbeIsNotPortal() throws Exception { 1171 setSslException(mHttpsConnection); 1172 setStatus(mHttpConnection, 500); 1173 setStatus(mFallbackConnection, 500); 1174 1175 // Fallback probe did not see portal, HTTPS failed -> inconclusive 1176 runFailedNetworkTest(); 1177 } 1178 1179 @Test testIsCaptivePortal_OtherFallbackProbeIsPortal()1180 public void testIsCaptivePortal_OtherFallbackProbeIsPortal() throws Exception { 1181 // Set all fallback probes but one to invalid URLs to verify they are being skipped 1182 setFallbackUrl(TEST_FALLBACK_URL); 1183 setOtherFallbackUrls(TEST_FALLBACK_URL + "," + TEST_OTHER_FALLBACK_URL); 1184 1185 setSslException(mHttpsConnection); 1186 setStatus(mHttpConnection, 500); 1187 setStatus(mFallbackConnection, 500); 1188 setPortal302(mOtherFallbackConnection); 1189 1190 // TEST_OTHER_FALLBACK_URL is third 1191 when(mRandom.nextInt()).thenReturn(2); 1192 1193 // First check always uses the first fallback URL: inconclusive 1194 final NetworkMonitor monitor = runFailedNetworkTest(); 1195 verify(mFallbackConnection, times(1)).getResponseCode(); 1196 verify(mOtherFallbackConnection, never()).getResponseCode(); 1197 1198 // Second check should be triggered automatically after the reevaluate delay, and uses the 1199 // URL chosen by mRandom 1200 // Ensure that the reevaluate delay is not changed to a large value, otherwise this test 1201 // would block for too long and a different test strategy should be used. 1202 assertTrue(INITIAL_REEVALUATE_DELAY_MS < 2000); 1203 verify(mOtherFallbackConnection, timeout(INITIAL_REEVALUATE_DELAY_MS + HANDLER_TIMEOUT_MS)) 1204 .getResponseCode(); 1205 verifyNetworkTested(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, TEST_LOGIN_URL); 1206 } 1207 1208 @Test 1209 public void testIsCaptivePortal_AllProbesFailed() throws Exception { 1210 setSslException(mHttpsConnection); 1211 setStatus(mHttpConnection, 500); 1212 setStatus(mFallbackConnection, 404); 1213 1214 runFailedNetworkTest(); 1215 verify(mFallbackConnection, times(1)).getResponseCode(); 1216 verify(mOtherFallbackConnection, never()).getResponseCode(); 1217 } 1218 1219 @Test 1220 public void testIsCaptivePortal_InvalidUrlSkipped() throws Exception { 1221 setFallbackUrl("invalid"); 1222 setOtherFallbackUrls("otherinvalid," + TEST_OTHER_FALLBACK_URL + ",yetanotherinvalid"); 1223 1224 setSslException(mHttpsConnection); 1225 setStatus(mHttpConnection, 500); 1226 setPortal302(mOtherFallbackConnection); 1227 runPortalNetworkTest(); 1228 verify(mOtherFallbackConnection, times(1)).getResponseCode(); 1229 verify(mFallbackConnection, never()).getResponseCode(); 1230 } 1231 1232 @Test 1233 public void testIsCaptivePortal_CapportApiIsPortalWithNullPortalUrl() throws Exception { 1234 assumeTrue(CaptivePortalDataShimImpl.isSupported()); 1235 setSslException(mHttpsConnection); 1236 final long bytesRemaining = 10_000L; 1237 final long secondsRemaining = 500L; 1238 // Set content without partal url. 1239 setApiContent(mCapportApiConnection, "{'captive': true," 1240 + "'venue-info-url': '" + TEST_VENUE_INFO_URL + "'," 1241 + "'bytes-remaining': " + bytesRemaining + "," 1242 + "'seconds-remaining': " + secondsRemaining + "}"); 1243 setPortal302(mHttpConnection); 1244 1245 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES, VALIDATION_RESULT_PORTAL, 1246 0 /* probesSucceeded*/, TEST_LOGIN_URL); 1247 1248 verify(mCapportApiConnection).getResponseCode(); 1249 1250 verify(mHttpConnection, times(1)).getResponseCode(); 1251 verify(mCallbacks, never()).notifyCaptivePortalDataChanged(any()); 1252 } 1253 1254 @Test 1255 public void testIsCaptivePortal_CapportApiIsPortalWithValidPortalUrl() throws Exception { 1256 assumeTrue(CaptivePortalDataShimImpl.isSupported()); 1257 setSslException(mHttpsConnection); 1258 final long bytesRemaining = 10_000L; 1259 final long secondsRemaining = 500L; 1260 1261 setApiContent(mCapportApiConnection, "{'captive': true," 1262 + "'user-portal-url': '" + TEST_LOGIN_URL + "'," 1263 + "'venue-info-url': '" + TEST_VENUE_INFO_URL + "'," 1264 + "'bytes-remaining': " + bytesRemaining + "," 1265 + "'seconds-remaining': " + secondsRemaining + "}"); 1266 1267 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES, VALIDATION_RESULT_PORTAL, 1268 0 /* probesSucceeded*/, TEST_LOGIN_URL); 1269 1270 verify(mHttpConnection, never()).getResponseCode(); 1271 verify(mCapportApiConnection).getResponseCode(); 1272 1273 final ArgumentCaptor<CaptivePortalData> capportDataCaptor = 1274 ArgumentCaptor.forClass(CaptivePortalData.class); 1275 verify(mCallbacks).notifyCaptivePortalDataChanged(capportDataCaptor.capture()); 1276 final CaptivePortalData p = capportDataCaptor.getValue(); 1277 assertTrue(p.isCaptive()); 1278 assertEquals(Uri.parse(TEST_LOGIN_URL), p.getUserPortalUrl()); 1279 assertEquals(Uri.parse(TEST_VENUE_INFO_URL), p.getVenueInfoUrl()); 1280 assertEquals(bytesRemaining, p.getByteLimit()); 1281 final long expectedExpiry = currentTimeMillis() + secondsRemaining * 1000; 1282 // Actual expiry will be slightly lower as some time as passed 1283 assertTrue(p.getExpiryTimeMillis() <= expectedExpiry); 1284 assertTrue(p.getExpiryTimeMillis() > expectedExpiry - 30_000); 1285 } 1286 1287 @Test 1288 public void testIsCaptivePortal_CapportApiRevalidation() throws Exception { 1289 assumeTrue(CaptivePortalDataShimImpl.isSupported()); 1290 setValidProbes(); 1291 final NetworkMonitor nm = runValidatedNetworkTest(); 1292 1293 setApiContent(mCapportApiConnection, "{'captive': true, " 1294 + "'user-portal-url': '" + TEST_LOGIN_URL + "'}"); 1295 nm.notifyLinkPropertiesChanged(makeCapportLPs()); 1296 1297 verifyNetworkTested(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, 1298 TEST_LOGIN_URL); 1299 final ArgumentCaptor<CaptivePortalData> capportCaptor = ArgumentCaptor.forClass( 1300 CaptivePortalData.class); 1301 verify(mCallbacks).notifyCaptivePortalDataChanged(capportCaptor.capture()); 1302 assertEquals(Uri.parse(TEST_LOGIN_URL), capportCaptor.getValue().getUserPortalUrl()); 1303 1304 // HTTP probe was sent on first validation but not re-sent when there was a portal URL. 1305 verify(mHttpConnection, times(1)).getResponseCode(); 1306 verify(mCapportApiConnection, times(1)).getResponseCode(); 1307 } 1308 1309 @Test 1310 public void testIsCaptivePortal_NoRevalidationBeforeNetworkConnected() throws Exception { 1311 assumeTrue(CaptivePortalDataShimImpl.isSupported()); 1312 1313 final NetworkMonitor nm = makeCellMeteredNetworkMonitor(); 1314 1315 final LinkProperties lp = makeCapportLPs(); 1316 1317 // LinkProperties changed, but NM should not revalidate before notifyNetworkConnected 1318 nm.notifyLinkPropertiesChanged(lp); 1319 verify(mHttpConnection, after(100).never()).getResponseCode(); 1320 verify(mHttpsConnection, never()).getResponseCode(); 1321 verify(mCapportApiConnection, never()).getResponseCode(); 1322 1323 setValidProbes(); 1324 setApiContent(mCapportApiConnection, "{'captive': true, " 1325 + "'user-portal-url': '" + TEST_LOGIN_URL + "'}"); 1326 1327 // After notifyNetworkConnected, validation uses the capport API contents 1328 nm.notifyNetworkConnected(lp, CELL_METERED_CAPABILITIES); 1329 verifyNetworkTested(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, TEST_LOGIN_URL); 1330 1331 verify(mHttpConnection, never()).getResponseCode(); 1332 verify(mCapportApiConnection).getResponseCode(); 1333 } 1334 1335 @Test 1336 public void testIsCaptivePortal_CapportApiNotPortalNotValidated() throws Exception { 1337 assumeTrue(CaptivePortalDataShimImpl.isSupported()); 1338 setSslException(mHttpsConnection); 1339 setStatus(mHttpConnection, 500); 1340 setApiContent(mCapportApiConnection, "{'captive': false," 1341 + "'venue-info-url': '" + TEST_VENUE_INFO_URL + "'}"); 1342 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES, VALIDATION_RESULT_INVALID, 1343 0 /* probesSucceeded */, null /* redirectUrl */); 1344 1345 final ArgumentCaptor<CaptivePortalData> capportCaptor = ArgumentCaptor.forClass( 1346 CaptivePortalData.class); 1347 verify(mCallbacks).notifyCaptivePortalDataChanged(capportCaptor.capture()); 1348 assertEquals(Uri.parse(TEST_VENUE_INFO_URL), capportCaptor.getValue().getVenueInfoUrl()); 1349 } 1350 1351 @Test 1352 public void testIsCaptivePortal_CapportApiNotPortalPartial() throws Exception { 1353 assumeTrue(CaptivePortalDataShimImpl.isSupported()); 1354 setSslException(mHttpsConnection); 1355 setStatus(mHttpConnection, 204); 1356 setApiContent(mCapportApiConnection, "{'captive': false," 1357 + "'venue-info-url': '" + TEST_VENUE_INFO_URL + "'}"); 1358 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES, 1359 NETWORK_VALIDATION_RESULT_PARTIAL, 1360 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP, 1361 null /* redirectUrl */); 1362 1363 final ArgumentCaptor<CaptivePortalData> capportCaptor = ArgumentCaptor.forClass( 1364 CaptivePortalData.class); 1365 verify(mCallbacks).notifyCaptivePortalDataChanged(capportCaptor.capture()); 1366 assertEquals(Uri.parse(TEST_VENUE_INFO_URL), capportCaptor.getValue().getVenueInfoUrl()); 1367 } 1368 1369 @Test 1370 public void testIsCaptivePortal_CapportApiNotPortalValidated() throws Exception { 1371 assumeTrue(CaptivePortalDataShimImpl.isSupported()); 1372 setStatus(mHttpsConnection, 204); 1373 setStatus(mHttpConnection, 204); 1374 setApiContent(mCapportApiConnection, "{'captive': false," 1375 + "'venue-info-url': '" + TEST_VENUE_INFO_URL + "'}"); 1376 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES, 1377 NETWORK_VALIDATION_RESULT_VALID, 1378 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP 1379 | NETWORK_VALIDATION_PROBE_HTTPS, 1380 null /* redirectUrl */); 1381 1382 final ArgumentCaptor<CaptivePortalData> capportCaptor = ArgumentCaptor.forClass( 1383 CaptivePortalData.class); 1384 verify(mCallbacks).notifyCaptivePortalDataChanged(capportCaptor.capture()); 1385 assertEquals(Uri.parse(TEST_VENUE_INFO_URL), capportCaptor.getValue().getVenueInfoUrl()); 1386 } 1387 1388 @Test 1389 public void testIsCaptivePortal_CapportApiInvalidContent() throws Exception { 1390 assumeTrue(CaptivePortalDataShimImpl.isSupported()); 1391 setSslException(mHttpsConnection); 1392 setPortal302(mHttpConnection); 1393 setApiContent(mCapportApiConnection, "{SomeInvalidText"); 1394 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES, 1395 VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, 1396 TEST_LOGIN_URL); 1397 1398 verify(mCallbacks, never()).notifyCaptivePortalDataChanged(any()); 1399 verify(mHttpConnection).getResponseCode(); 1400 } 1401 1402 private void runCapportApiInvalidUrlTest(String url) throws Exception { 1403 assumeTrue(CaptivePortalDataShimImpl.isSupported()); 1404 setSslException(mHttpsConnection); 1405 setPortal302(mHttpConnection); 1406 final LinkProperties lp = new LinkProperties(TEST_LINK_PROPERTIES); 1407 lp.setCaptivePortalApiUrl(Uri.parse(url)); 1408 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES, 1409 VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, 1410 TEST_LOGIN_URL); 1411 1412 verify(mCallbacks, never()).notifyCaptivePortalDataChanged(any()); 1413 verify(mCapportApiConnection, never()).getInputStream(); 1414 verify(mHttpConnection).getResponseCode(); 1415 } 1416 1417 @Test 1418 public void testIsCaptivePortal_HttpIsInvalidCapportApiScheme() throws Exception { 1419 runCapportApiInvalidUrlTest("http://capport.example.com"); 1420 } 1421 1422 @Test 1423 public void testIsCaptivePortal_FileIsInvalidCapportApiScheme() throws Exception { 1424 runCapportApiInvalidUrlTest("file://localhost/myfile"); 1425 } 1426 1427 @Test 1428 public void testIsCaptivePortal_InvalidUrlFormat() throws Exception { 1429 runCapportApiInvalidUrlTest("ThisIsNotAValidUrl"); 1430 } 1431 1432 @Test @IgnoreUpTo(Build.VERSION_CODES.Q) 1433 public void testIsCaptivePortal_CapportApiNotSupported() throws Exception { 1434 // Test that on a R+ device, if NetworkStack was compiled without CaptivePortalData support 1435 // (built against Q), NetworkMonitor behaves as expected. 1436 assumeFalse(CaptivePortalDataShimImpl.isSupported()); 1437 setSslException(mHttpsConnection); 1438 setPortal302(mHttpConnection); 1439 setApiContent(mCapportApiConnection, "{'captive': false," 1440 + "'venue-info-url': '" + TEST_VENUE_INFO_URL + "'}"); 1441 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES, VALIDATION_RESULT_PORTAL, 1442 0 /* probesSucceeded */, 1443 TEST_LOGIN_URL); 1444 1445 verify(mCallbacks, never()).notifyCaptivePortalDataChanged(any()); 1446 verify(mHttpConnection).getResponseCode(); 1447 } 1448 1449 @Test 1450 public void testIsCaptivePortal_HttpsProbeMatchesFailRegex() throws Exception { 1451 setStatus(mHttpsConnection, 200); 1452 setStatus(mHttpConnection, 500); 1453 final String content = "test"; 1454 doReturn(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))) 1455 .when(mHttpsConnection).getInputStream(); 1456 doReturn(Long.valueOf(content.length())).when(mHttpsConnection).getContentLengthLong(); 1457 doReturn(1).when(mResources).getInteger(R.integer.config_min_matches_http_content_length); 1458 doReturn(10).when(mResources).getInteger( 1459 R.integer.config_max_matches_http_content_length); 1460 doReturn("te.t").when(mResources).getString( 1461 R.string.config_network_validation_failed_content_regexp); 1462 runFailedNetworkTest(); 1463 } 1464 1465 @Test 1466 public void testIsCaptivePortal_HttpProbeMatchesSuccessRegex() throws Exception { 1467 setStatus(mHttpsConnection, 500); 1468 setStatus(mHttpConnection, 200); 1469 final String content = "test"; 1470 doReturn(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))) 1471 .when(mHttpConnection).getInputStream(); 1472 doReturn(Long.valueOf(content.length())).when(mHttpConnection).getContentLengthLong(); 1473 doReturn(1).when(mResources).getInteger(R.integer.config_min_matches_http_content_length); 1474 doReturn(10).when(mResources).getInteger( 1475 R.integer.config_max_matches_http_content_length); 1476 doReturn("te.t").when(mResources).getString( 1477 R.string.config_network_validation_success_content_regexp); 1478 runPartialConnectivityNetworkTest( 1479 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP); 1480 } 1481 1482 private void setupFallbackSpec() throws IOException { 1483 setFallbackSpecs("http://example.com@@/@@204@@/@@" 1484 + "@@,@@" 1485 + TEST_OTHER_FALLBACK_URL + "@@/@@30[12]@@/@@https://(www\\.)?google.com/?.*"); 1486 1487 setSslException(mHttpsConnection); 1488 setStatus(mHttpConnection, 500); 1489 1490 // Use the 2nd fallback spec 1491 when(mRandom.nextInt()).thenReturn(1); 1492 } 1493 1494 @Test 1495 public void testIsCaptivePortal_FallbackSpecIsFail() throws Exception { 1496 setupFallbackSpec(); 1497 set302(mOtherFallbackConnection, "https://www.google.com/test?q=3"); 1498 1499 runNetworkTest(VALIDATION_RESULT_INVALID, 1500 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_FALLBACK, 1501 null /* redirectUrl */); 1502 verify(mOtherFallbackConnection, times(1)).getResponseCode(); 1503 verify(mFallbackConnection, never()).getResponseCode(); 1504 } 1505 1506 @Test 1507 public void testIsCaptivePortal_FallbackSpecIsPortal() throws Exception { 1508 setupFallbackSpec(); 1509 setPortal302(mOtherFallbackConnection); 1510 runPortalNetworkTest(); 1511 } 1512 1513 @Test 1514 public void testIsCaptivePortal_IgnorePortals() throws Exception { 1515 setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE); 1516 setSslException(mHttpsConnection); 1517 setPortal302(mHttpConnection); 1518 1519 runNoValidationNetworkTest(); 1520 } 1521 1522 @Test 1523 public void testIsCaptivePortal_OverriddenHttpsUrlValid() throws Exception { 1524 setDeviceConfig(TEST_URL_EXPIRATION_TIME, 1525 String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(9))); 1526 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL, TEST_OVERRIDE_URL); 1527 setStatus(mTestOverriddenUrlConnection, 204); 1528 setStatus(mHttpConnection, 204); 1529 1530 runValidatedNetworkTest(); 1531 verify(mHttpsConnection, never()).getResponseCode(); 1532 verify(mTestOverriddenUrlConnection).getResponseCode(); 1533 } 1534 1535 @Test 1536 public void testIsCaptivePortal_OverriddenHttpUrlPortal() throws Exception { 1537 setDeviceConfig(TEST_URL_EXPIRATION_TIME, 1538 String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(9))); 1539 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTP_URL, TEST_OVERRIDE_URL); 1540 setStatus(mHttpsConnection, 500); 1541 setPortal302(mTestOverriddenUrlConnection); 1542 1543 runPortalNetworkTest(); 1544 verify(mHttpConnection, never()).getResponseCode(); 1545 verify(mTestOverriddenUrlConnection).getResponseCode(); 1546 } 1547 1548 @Test 1549 public void testIsCaptivePortal_InvalidHttpOverrideUrl() throws Exception { 1550 setDeviceConfig(TEST_URL_EXPIRATION_TIME, 1551 String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(9))); 1552 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTP_URL, TEST_INVALID_OVERRIDE_URL); 1553 setStatus(mHttpsConnection, 500); 1554 setPortal302(mHttpConnection); 1555 1556 runPortalNetworkTest(); 1557 verify(mTestOverriddenUrlConnection, never()).getResponseCode(); 1558 verify(mHttpConnection).getResponseCode(); 1559 } 1560 1561 @Test 1562 public void testIsCaptivePortal_InvalidHttpsOverrideUrl() throws Exception { 1563 setDeviceConfig(TEST_URL_EXPIRATION_TIME, 1564 String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(9))); 1565 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL, TEST_INVALID_OVERRIDE_URL); 1566 setStatus(mHttpsConnection, 204); 1567 setStatus(mHttpConnection, 204); 1568 1569 runValidatedNetworkTest(); 1570 verify(mTestOverriddenUrlConnection, never()).getResponseCode(); 1571 verify(mHttpsConnection).getResponseCode(); 1572 } 1573 1574 @Test 1575 public void testIsCaptivePortal_ExpiredHttpsOverrideUrl() throws Exception { 1576 setDeviceConfig(TEST_URL_EXPIRATION_TIME, 1577 String.valueOf(currentTimeMillis() - TimeUnit.MINUTES.toMillis(1))); 1578 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL, TEST_OVERRIDE_URL); 1579 setStatus(mHttpsConnection, 204); 1580 setStatus(mHttpConnection, 204); 1581 1582 runValidatedNetworkTest(); 1583 verify(mTestOverriddenUrlConnection, never()).getResponseCode(); 1584 verify(mHttpsConnection).getResponseCode(); 1585 } 1586 1587 @Test 1588 public void testIsCaptivePortal_TestHttpUrlExpirationTooLarge() throws Exception { 1589 setDeviceConfig(TEST_URL_EXPIRATION_TIME, 1590 String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(20))); 1591 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTP_URL, TEST_OVERRIDE_URL); 1592 setStatus(mHttpsConnection, 500); 1593 setPortal302(mHttpConnection); 1594 1595 runPortalNetworkTest(); 1596 verify(mTestOverriddenUrlConnection, never()).getResponseCode(); 1597 verify(mHttpConnection).getResponseCode(); 1598 } 1599 1600 @Test 1601 public void testIsCaptivePortal_TestUrlsWithUrlOverlays() throws Exception { 1602 setupResourceForMultipleProbes(); 1603 doReturn(TEST_HTTPS_URL).when(mResources) 1604 .getString(R.string.config_captive_portal_https_url); 1605 doReturn(TEST_HTTP_URL).when(mResources) 1606 .getString(R.string.config_captive_portal_http_url); 1607 1608 setDeviceConfig(TEST_URL_EXPIRATION_TIME, 1609 String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(9))); 1610 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL, TEST_OVERRIDE_URL); 1611 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTP_URL, TEST_OVERRIDE_URL); 1612 setStatus(mTestOverriddenUrlConnection, 204); 1613 1614 runValidatedNetworkTest(); 1615 verify(mHttpsConnection, never()).getResponseCode(); 1616 verify(mHttpConnection, never()).getResponseCode(); 1617 verify(mOtherHttpsConnection1, never()).getResponseCode(); 1618 verify(mOtherHttpsConnection2, never()).getResponseCode(); 1619 verify(mOtherHttpConnection1, never()).getResponseCode(); 1620 verify(mOtherHttpConnection2, never()).getResponseCode(); 1621 1622 // Used for both HTTP and HTTPS: can be called once (if HTTPS validates first) or twice 1623 verify(mTestOverriddenUrlConnection, atLeastOnce()).getResponseCode(); 1624 } 1625 1626 @Test 1627 public void testIsDataStall_EvaluationDisabled() { 1628 setDataStallEvaluationType(0); 1629 WrappedNetworkMonitor wrappedMonitor = makeCellMeteredNetworkMonitor(); 1630 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); 1631 assertFalse(wrappedMonitor.isDataStall()); 1632 } 1633 1634 @Test 1635 public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() throws Exception { 1636 WrappedNetworkMonitor wrappedMonitor = makeCellNotMeteredNetworkMonitor(); 1637 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); 1638 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); 1639 assertTrue(wrappedMonitor.isDataStall()); 1640 verify(mCallbacks).notifyDataStallSuspected( 1641 matchDnsDataStallParcelable(DEFAULT_DNS_TIMEOUT_THRESHOLD)); 1642 } 1643 1644 @Test 1645 public void testIsDataStall_EvaluationDnsOnMeteredNetwork() throws Exception { 1646 WrappedNetworkMonitor wrappedMonitor = makeCellMeteredNetworkMonitor(); 1647 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); 1648 assertFalse(wrappedMonitor.isDataStall()); 1649 1650 wrappedMonitor.setLastProbeTime( 1651 SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); 1652 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); 1653 assertTrue(wrappedMonitor.isDataStall()); 1654 verify(mCallbacks).notifyDataStallSuspected( 1655 matchDnsDataStallParcelable(DEFAULT_DNS_TIMEOUT_THRESHOLD)); 1656 } 1657 1658 @Test 1659 public void testIsDataStall_EvaluationDnsWithDnsTimeoutCount() throws Exception { 1660 WrappedNetworkMonitor wrappedMonitor = makeCellMeteredNetworkMonitor(); 1661 wrappedMonitor.setLastProbeTime( 1662 SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); 1663 makeDnsTimeoutEvent(wrappedMonitor, 3); 1664 assertFalse(wrappedMonitor.isDataStall()); 1665 // Reset consecutive timeout counts. 1666 makeDnsSuccessEvent(wrappedMonitor, 1); 1667 makeDnsTimeoutEvent(wrappedMonitor, 2); 1668 assertFalse(wrappedMonitor.isDataStall()); 1669 1670 makeDnsTimeoutEvent(wrappedMonitor, 3); 1671 assertTrue(wrappedMonitor.isDataStall()); 1672 1673 // The expected timeout count is the previous 2 DNS timeouts + the most recent 3 timeouts 1674 verify(mCallbacks).notifyDataStallSuspected( 1675 matchDnsDataStallParcelable(5 /* timeoutCount */)); 1676 1677 // Set the value to larger than the default dns log size. 1678 setConsecutiveDnsTimeoutThreshold(51); 1679 wrappedMonitor = makeCellMeteredNetworkMonitor(); 1680 wrappedMonitor.setLastProbeTime( 1681 SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); 1682 makeDnsTimeoutEvent(wrappedMonitor, 50); 1683 assertFalse(wrappedMonitor.isDataStall()); 1684 1685 makeDnsTimeoutEvent(wrappedMonitor, 1); 1686 assertTrue(wrappedMonitor.isDataStall()); 1687 1688 // The expected timeout count is the previous 50 DNS timeouts + the most recent timeout 1689 verify(mCallbacks).notifyDataStallSuspected( 1690 matchDnsDataStallParcelable(51 /* timeoutCount */)); 1691 } 1692 1693 @Test 1694 public void testIsDataStall_SkipEvaluateOnValidationNotRequiredNetwork() { 1695 // Make DNS and TCP stall condition satisfied. 1696 setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS | DATA_STALL_EVALUATION_TYPE_TCP); 1697 when(mTstDependencies.isTcpInfoParsingSupported()).thenReturn(true); 1698 when(mTst.getLatestReceivedCount()).thenReturn(0); 1699 when(mTst.isDataStallSuspected()).thenReturn(true); 1700 final WrappedNetworkMonitor nm = makeMonitor(CELL_NO_INTERNET_CAPABILITIES); 1701 nm.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); 1702 makeDnsTimeoutEvent(nm, DEFAULT_DNS_TIMEOUT_THRESHOLD); 1703 assertFalse(nm.isDataStall()); 1704 } 1705 1706 @Test 1707 public void testIsDataStall_EvaluationDnsWithDnsTimeThreshold() throws Exception { 1708 // Test dns events happened in valid dns time threshold. 1709 WrappedNetworkMonitor wrappedMonitor = makeCellMeteredNetworkMonitor(); 1710 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); 1711 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); 1712 assertFalse(wrappedMonitor.isDataStall()); 1713 wrappedMonitor.setLastProbeTime( 1714 SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); 1715 assertTrue(wrappedMonitor.isDataStall()); 1716 verify(mCallbacks).notifyDataStallSuspected( 1717 matchDnsDataStallParcelable(DEFAULT_DNS_TIMEOUT_THRESHOLD)); 1718 1719 // Test dns events happened before valid dns time threshold. 1720 setValidDataStallDnsTimeThreshold(0); 1721 wrappedMonitor = makeCellMeteredNetworkMonitor(); 1722 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); 1723 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); 1724 assertFalse(wrappedMonitor.isDataStall()); 1725 wrappedMonitor.setLastProbeTime( 1726 SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); 1727 assertFalse(wrappedMonitor.isDataStall()); 1728 } 1729 1730 @Test 1731 public void testIsDataStall_EvaluationTcp() throws Exception { 1732 // Evaluate TCP only. Expect ignoring DNS signal. 1733 setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_TCP); 1734 WrappedNetworkMonitor wrappedMonitor = makeMonitor(CELL_METERED_CAPABILITIES); 1735 assertFalse(wrappedMonitor.isDataStall()); 1736 // Packet received. 1737 when(mTstDependencies.isTcpInfoParsingSupported()).thenReturn(true); 1738 when(mTst.getLatestReceivedCount()).thenReturn(5); 1739 // Trigger a tcp event immediately. 1740 setTcpPollingInterval(0); 1741 wrappedMonitor.sendTcpPollingEvent(); 1742 HandlerUtils.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS); 1743 assertFalse(wrappedMonitor.isDataStall()); 1744 1745 when(mTst.getLatestReceivedCount()).thenReturn(0); 1746 when(mTst.isDataStallSuspected()).thenReturn(true); 1747 // Trigger a tcp event immediately. 1748 setTcpPollingInterval(0); 1749 wrappedMonitor.sendTcpPollingEvent(); 1750 HandlerUtils.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS); 1751 assertTrue(wrappedMonitor.isDataStall()); 1752 verify(mCallbacks).notifyDataStallSuspected(matchTcpDataStallParcelable()); 1753 } 1754 1755 @Test 1756 public void testIsDataStall_EvaluationDnsAndTcp() throws Exception { 1757 setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS | DATA_STALL_EVALUATION_TYPE_TCP); 1758 setupTcpDataStall(); 1759 final WrappedNetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES); 1760 nm.setLastProbeTime(SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); 1761 makeDnsTimeoutEvent(nm, DEFAULT_DNS_TIMEOUT_THRESHOLD); 1762 assertTrue(nm.isDataStall()); 1763 verify(mCallbacks).notifyDataStallSuspected( 1764 matchDnsAndTcpDataStallParcelable(DEFAULT_DNS_TIMEOUT_THRESHOLD)); 1765 1766 when(mTst.getLatestReceivedCount()).thenReturn(5); 1767 // Trigger a tcp event immediately. 1768 setTcpPollingInterval(0); 1769 nm.sendTcpPollingEvent(); 1770 HandlerUtils.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS); 1771 assertFalse(nm.isDataStall()); 1772 } 1773 1774 @Test 1775 public void testIsDataStall_DisableTcp() { 1776 // Disable tcp detection with only DNS detect. keep the tcp signal but set to no DNS signal. 1777 setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS); 1778 WrappedNetworkMonitor wrappedMonitor = makeMonitor(CELL_METERED_CAPABILITIES); 1779 makeDnsSuccessEvent(wrappedMonitor, 1); 1780 wrappedMonitor.sendTcpPollingEvent(); 1781 HandlerUtils.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS); 1782 assertFalse(wrappedMonitor.isDataStall()); 1783 verify(mTst, never()).isDataStallSuspected(); 1784 verify(mTst, never()).pollSocketsInfo(); 1785 } 1786 1787 @Test 1788 public void testBrokenNetworkNotValidated() throws Exception { 1789 setSslException(mHttpsConnection); 1790 setStatus(mHttpConnection, 500); 1791 setStatus(mFallbackConnection, 404); 1792 1793 runFailedNetworkTest(); 1794 } 1795 1796 @Test 1797 public void testNoInternetCapabilityValidated() throws Exception { 1798 runNetworkTest(TEST_LINK_PROPERTIES, CELL_NO_INTERNET_CAPABILITIES, 1799 NETWORK_VALIDATION_RESULT_VALID, 0 /* probesSucceeded */, null /* redirectUrl */); 1800 verify(mCleartextDnsNetwork, never()).openConnection(any()); 1801 } 1802 1803 private NetworkCapabilities getVcnUnderlyingCarrierWifiCaps() { 1804 // Must be called from within the test because NOT_VCN_MANAGED is an invalid capability 1805 // value up to Android R. Thus, this must be guarded by an SDK check in tests that use this. 1806 return new NetworkCapabilities.Builder() 1807 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) 1808 .removeCapability(NetworkMonitorUtils.NET_CAPABILITY_NOT_VCN_MANAGED) 1809 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) 1810 .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) 1811 .addCapability(NET_CAPABILITY_INTERNET) 1812 .build(); 1813 } 1814 1815 @Test 1816 public void testVcnUnderlyingNetwork() throws Exception { 1817 assumeTrue(ShimUtils.isAtLeastS()); 1818 setStatus(mHttpsConnection, 204); 1819 setStatus(mHttpConnection, 204); 1820 1821 final NetworkMonitor nm = runNetworkTest( 1822 TEST_LINK_PROPERTIES, getVcnUnderlyingCarrierWifiCaps(), 1823 NETWORK_VALIDATION_RESULT_VALID, 1824 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS, 1825 null /* redirectUrl */); 1826 assertEquals(NETWORK_VALIDATION_RESULT_VALID, 1827 nm.getEvaluationState().getEvaluationResult()); 1828 } 1829 1830 @Test 1831 public void testVcnUnderlyingNetworkBadNetwork() throws Exception { 1832 assumeTrue(ShimUtils.isAtLeastS()); 1833 setSslException(mHttpsConnection); 1834 setStatus(mHttpConnection, 500); 1835 setStatus(mFallbackConnection, 404); 1836 1837 final NetworkMonitor nm = runNetworkTest( 1838 TEST_LINK_PROPERTIES, getVcnUnderlyingCarrierWifiCaps(), 1839 VALIDATION_RESULT_INVALID, 0 /* probesSucceeded */, null /* redirectUrl */); 1840 assertEquals(VALIDATION_RESULT_INVALID, 1841 nm.getEvaluationState().getEvaluationResult()); 1842 } 1843 1844 @Test 1845 public void testLaunchCaptivePortalApp() throws Exception { 1846 setSslException(mHttpsConnection); 1847 setPortal302(mHttpConnection); 1848 when(mHttpConnection.getHeaderField(eq("location"))).thenReturn(TEST_LOGIN_URL); 1849 final NetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES); 1850 notifyNetworkConnected(nm, CELL_METERED_CAPABILITIES); 1851 1852 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) 1853 .showProvisioningNotification(any(), any()); 1854 1855 assertEquals(1, mRegisteredReceivers.size()); 1856 1857 // Check that startCaptivePortalApp sends the expected intent. 1858 nm.launchCaptivePortalApp(); 1859 1860 final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); 1861 final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class); 1862 verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1)) 1863 .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture()); 1864 verify(mNotifier).notifyCaptivePortalValidationPending(networkCaptor.getValue()); 1865 final Bundle bundle = bundleCaptor.getValue(); 1866 final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK); 1867 assertEquals(TEST_NETID, bundleNetwork.netId); 1868 // network is passed both in bundle and as parameter, as the bundle is opaque to the 1869 // framework and only intended for the captive portal app, but the framework needs 1870 // the network to identify the right NetworkMonitor. 1871 assertEquals(TEST_NETID, networkCaptor.getValue().netId); 1872 // Portal URL should be detection URL. 1873 final String redirectUrl = bundle.getString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL); 1874 assertEquals(TEST_HTTP_URL, redirectUrl); 1875 1876 // Have the app report that the captive portal is dismissed, and check that we revalidate. 1877 setStatus(mHttpsConnection, 204); 1878 setStatus(mHttpConnection, 204); 1879 1880 resetCallbacks(); 1881 nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED); 1882 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()) 1883 .notifyNetworkTestedWithExtras(matchNetworkTestResultParcelable( 1884 NETWORK_VALIDATION_RESULT_VALID, 1885 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP)); 1886 assertEquals(0, mRegisteredReceivers.size()); 1887 } 1888 1889 @Test 1890 public void testPrivateDnsSuccess() throws Exception { 1891 setStatus(mHttpsConnection, 204); 1892 setStatus(mHttpConnection, 204); 1893 1894 // Verify dns query only get v6 address. 1895 mFakeDns.setAnswer("dns6.google", new String[]{"2001:db8::53"}, TYPE_AAAA); 1896 WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); 1897 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns6.google", 1898 new InetAddress[0])); 1899 notifyNetworkConnected(wnm, CELL_NOT_METERED_CAPABILITIES); 1900 verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID, PROBES_PRIVDNS_VALID); 1901 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged( 1902 eq(PROBES_PRIVDNS_VALID), eq(PROBES_PRIVDNS_VALID)); 1903 1904 // Verify dns query only get v4 address. 1905 resetCallbacks(); 1906 mFakeDns.setAnswer("dns4.google", new String[]{"192.0.2.1"}, TYPE_A); 1907 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns4.google", 1908 new InetAddress[0])); 1909 verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID, PROBES_PRIVDNS_VALID); 1910 // NetworkMonitor will check if the probes has changed or not, if the probes has not 1911 // changed, the callback won't be fired. 1912 verify(mCallbacks, never()).notifyProbeStatusChanged( 1913 eq(PROBES_PRIVDNS_VALID), eq(PROBES_PRIVDNS_VALID)); 1914 1915 // Verify dns query get both v4 and v6 address. 1916 resetCallbacks(); 1917 mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::54"}, TYPE_AAAA); 1918 mFakeDns.setAnswer("dns.google", new String[]{"192.0.2.3"}, TYPE_A); 1919 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0])); 1920 verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID, PROBES_PRIVDNS_VALID); 1921 verify(mCallbacks, never()).notifyProbeStatusChanged( 1922 eq(PROBES_PRIVDNS_VALID), eq(PROBES_PRIVDNS_VALID)); 1923 } 1924 1925 @Test 1926 public void testProbeStatusChanged() throws Exception { 1927 // Set no record in FakeDns and expect validation to fail. 1928 setStatus(mHttpsConnection, 204); 1929 setStatus(mHttpConnection, 204); 1930 1931 WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); 1932 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0])); 1933 wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, CELL_NOT_METERED_CAPABILITIES); 1934 verifyNetworkTested(VALIDATION_RESULT_INVALID, 1935 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS); 1936 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)).notifyProbeStatusChanged( 1937 eq(PROBES_PRIVDNS_VALID), eq(NETWORK_VALIDATION_PROBE_DNS 1938 | NETWORK_VALIDATION_PROBE_HTTPS)); 1939 // Fix DNS and retry, expect validation to succeed. 1940 resetCallbacks(); 1941 mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::1"}, TYPE_AAAA); 1942 1943 wnm.forceReevaluation(Process.myUid()); 1944 // ProbeCompleted should be reset to 0 1945 HandlerUtils.waitForIdle(wnm.getHandler(), HANDLER_TIMEOUT_MS); 1946 assertEquals(wnm.getEvaluationState().getProbeCompletedResult(), 0); 1947 verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID, PROBES_PRIVDNS_VALID); 1948 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)).notifyProbeStatusChanged( 1949 eq(PROBES_PRIVDNS_VALID), eq(PROBES_PRIVDNS_VALID)); 1950 } 1951 1952 @Test 1953 public void testPrivateDnsResolutionRetryUpdate() throws Exception { 1954 // Set no record in FakeDns and expect validation to fail. 1955 setStatus(mHttpsConnection, 204); 1956 setStatus(mHttpConnection, 204); 1957 1958 WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); 1959 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0])); 1960 wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, CELL_NOT_METERED_CAPABILITIES); 1961 verifyNetworkTested(VALIDATION_RESULT_INVALID, 1962 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS); 1963 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged( 1964 eq(PROBES_PRIVDNS_VALID), eq(NETWORK_VALIDATION_PROBE_DNS 1965 | NETWORK_VALIDATION_PROBE_HTTPS)); 1966 1967 // Fix DNS and retry, expect validation to succeed. 1968 resetCallbacks(); 1969 mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::1"}, TYPE_AAAA); 1970 1971 wnm.forceReevaluation(Process.myUid()); 1972 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()) 1973 .notifyNetworkTestedWithExtras(matchNetworkTestResultParcelable( 1974 NETWORK_VALIDATION_RESULT_VALID, PROBES_PRIVDNS_VALID)); 1975 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged( 1976 eq(PROBES_PRIVDNS_VALID), eq(PROBES_PRIVDNS_VALID)); 1977 1978 // Change configuration to an invalid DNS name, expect validation to fail. 1979 resetCallbacks(); 1980 mFakeDns.setAnswer("dns.bad", new String[0], TYPE_A); 1981 mFakeDns.setAnswer("dns.bad", new String[0], TYPE_AAAA); 1982 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.bad", new InetAddress[0])); 1983 // Strict mode hostname resolve fail. Expect only notification for evaluation fail. No probe 1984 // notification. 1985 verifyNetworkTested(VALIDATION_RESULT_INVALID, 1986 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS); 1987 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged( 1988 eq(PROBES_PRIVDNS_VALID), eq(NETWORK_VALIDATION_PROBE_DNS 1989 | NETWORK_VALIDATION_PROBE_HTTPS)); 1990 1991 // Change configuration back to working again, but make private DNS not work. 1992 // Expect validation to fail. 1993 resetCallbacks(); 1994 mFakeDns.setNonBypassPrivateDnsWorking(false); 1995 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", 1996 new InetAddress[0])); 1997 verifyNetworkTested(VALIDATION_RESULT_INVALID, 1998 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS); 1999 // NetworkMonitor will check if the probes has changed or not, if the probes has not 2000 // changed, the callback won't be fired. 2001 verify(mCallbacks, never()).notifyProbeStatusChanged( 2002 eq(PROBES_PRIVDNS_VALID), eq(NETWORK_VALIDATION_PROBE_DNS 2003 | NETWORK_VALIDATION_PROBE_HTTPS)); 2004 2005 // Make private DNS work again. Expect validation to succeed. 2006 resetCallbacks(); 2007 mFakeDns.setNonBypassPrivateDnsWorking(true); 2008 wnm.forceReevaluation(Process.myUid()); 2009 verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID, PROBES_PRIVDNS_VALID); 2010 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged( 2011 eq(PROBES_PRIVDNS_VALID), eq(PROBES_PRIVDNS_VALID)); 2012 } 2013 2014 @Test 2015 public void testDataStall_StallDnsSuspectedAndSendMetricsOnCell() throws Exception { 2016 testDataStall_StallDnsSuspectedAndSendMetrics(NetworkCapabilities.TRANSPORT_CELLULAR, 2017 CELL_METERED_CAPABILITIES); 2018 } 2019 2020 @Test 2021 public void testDataStall_StallDnsSuspectedAndSendMetricsOnWifi() throws Exception { 2022 testDataStall_StallDnsSuspectedAndSendMetrics(NetworkCapabilities.TRANSPORT_WIFI, 2023 WIFI_NOT_METERED_CAPABILITIES); 2024 } 2025 2026 private void testDataStall_StallDnsSuspectedAndSendMetrics(int transport, 2027 NetworkCapabilities nc) throws Exception { 2028 // NM suspects data stall from DNS signal and sends data stall metrics. 2029 final WrappedNetworkMonitor nm = prepareNetworkMonitorForVerifyDataStall(nc); 2030 makeDnsTimeoutEvent(nm, 5); 2031 // Trigger a dns signal to start evaluate data stall and upload metrics. 2032 nm.notifyDnsResponse(RETURN_CODE_DNS_TIMEOUT); 2033 // Verify data sent as expected. 2034 verifySendDataStallDetectionStats(nm, DATA_STALL_EVALUATION_TYPE_DNS, transport); 2035 } 2036 2037 @Test 2038 public void testDataStall_NoStallSuspectedAndSendMetrics() throws Exception { 2039 final WrappedNetworkMonitor nm = prepareNetworkMonitorForVerifyDataStall( 2040 CELL_METERED_CAPABILITIES); 2041 // Setup no data stall dns signal. 2042 makeDnsTimeoutEvent(nm, 3); 2043 assertFalse(nm.isDataStall()); 2044 // Trigger a dns signal to start evaluate data stall. 2045 nm.notifyDnsResponse(RETURN_CODE_DNS_SUCCESS); 2046 verify(mDependencies, never()).writeDataStallDetectionStats(any(), any()); 2047 } 2048 2049 @Test 2050 public void testDataStall_StallTcpSuspectedAndSendMetricsOnCell() throws Exception { 2051 testDataStall_StallTcpSuspectedAndSendMetrics(CELL_METERED_CAPABILITIES); 2052 } 2053 2054 @Test 2055 public void testDataStall_StallTcpSuspectedAndSendMetricsOnWifi() throws Exception { 2056 testDataStall_StallTcpSuspectedAndSendMetrics(WIFI_NOT_METERED_CAPABILITIES); 2057 } 2058 2059 private void testDataStall_StallTcpSuspectedAndSendMetrics(NetworkCapabilities nc) 2060 throws Exception { 2061 assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)); 2062 setupTcpDataStall(); 2063 setTcpPollingInterval(0); 2064 // NM suspects data stall from TCP signal and sends data stall metrics. 2065 setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_TCP); 2066 final WrappedNetworkMonitor nm = prepareNetworkMonitorForVerifyDataStall(nc); 2067 // Trigger a tcp event immediately. 2068 nm.sendTcpPollingEvent(); 2069 // Allow only one transport type in the context of this test for simplification. 2070 final int[] transports = nc.getTransportTypes(); 2071 assertEquals(1, transports.length); 2072 verifySendDataStallDetectionStats(nm, DATA_STALL_EVALUATION_TYPE_TCP, transports[0]); 2073 } 2074 2075 private WrappedNetworkMonitor prepareNetworkMonitorForVerifyDataStall(NetworkCapabilities nc) 2076 throws Exception { 2077 // Connect a VALID network to simulate the data stall detection because data stall 2078 // evaluation will only start from validated state. 2079 setStatus(mHttpsConnection, 204); 2080 final WrappedNetworkMonitor nm; 2081 // Allow only one transport type in the context of this test for simplification. 2082 if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { 2083 nm = makeCellMeteredNetworkMonitor(); 2084 } else if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { 2085 nm = makeWifiNotMeteredNetworkMonitor(); 2086 setupTestWifiInfo(); 2087 } else { 2088 nm = null; 2089 fail("Undefined transport type"); 2090 } 2091 nm.notifyNetworkConnected(TEST_LINK_PROPERTIES, nc); 2092 verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID, 2093 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS); 2094 nm.setLastProbeTime(SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); 2095 return nm; 2096 } 2097 2098 private void setupTcpDataStall() { 2099 when(mTstDependencies.isTcpInfoParsingSupported()).thenReturn(true); 2100 when(mTst.getLatestReceivedCount()).thenReturn(0); 2101 when(mTst.getLatestPacketFailPercentage()).thenReturn(TEST_TCP_FAIL_RATE); 2102 when(mTst.getSentSinceLastRecv()).thenReturn(TEST_TCP_PACKET_COUNT); 2103 when(mTst.isDataStallSuspected()).thenReturn(true); 2104 when(mTst.pollSocketsInfo()).thenReturn(true); 2105 } 2106 2107 private void verifySendDataStallDetectionStats(WrappedNetworkMonitor nm, int evalType, 2108 int transport) { 2109 // Verify data sent as expectated. 2110 final ArgumentCaptor<CaptivePortalProbeResult> probeResultCaptor = 2111 ArgumentCaptor.forClass(CaptivePortalProbeResult.class); 2112 final ArgumentCaptor<DataStallDetectionStats> statsCaptor = 2113 ArgumentCaptor.forClass(DataStallDetectionStats.class); 2114 verify(mDependencies, timeout(HANDLER_TIMEOUT_MS).times(1)) 2115 .writeDataStallDetectionStats(statsCaptor.capture(), probeResultCaptor.capture()); 2116 // Ensure probe will not stop due to rate-limiting mechanism. 2117 nm.setLastProbeTime(SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); 2118 assertTrue(nm.isDataStall()); 2119 assertTrue(probeResultCaptor.getValue().isSuccessful()); 2120 verifyTestDataStallDetectionStats(evalType, transport, statsCaptor.getValue()); 2121 } 2122 2123 private void verifyTestDataStallDetectionStats(int evalType, int transport, 2124 DataStallDetectionStats stats) { 2125 assertEquals(transport, stats.mNetworkType); 2126 switch (transport) { 2127 case NetworkCapabilities.TRANSPORT_WIFI: 2128 assertArrayEquals(makeTestWifiDataNano(), stats.mWifiInfo); 2129 // Expedient way to check stats.mCellularInfo contains the neutral byte array that 2130 // is sent to represent a lack of data, as stats.mCellularInfo is not supposed to 2131 // contain null. 2132 assertArrayEquals(DataStallDetectionStats.emptyCellDataIfNull(null), 2133 stats.mCellularInfo); 2134 break; 2135 case NetworkCapabilities.TRANSPORT_CELLULAR: 2136 // Expedient way to check stats.mWifiInfo contains the neutral byte array that is 2137 // sent to represent a lack of data, as stats.mWifiInfo is not supposed to contain 2138 // null. 2139 assertArrayEquals(DataStallDetectionStats.emptyWifiInfoIfNull(null), 2140 stats.mWifiInfo); 2141 assertArrayEquals(makeTestCellDataNano(), stats.mCellularInfo); 2142 break; 2143 default: 2144 // Add other cases. 2145 fail("Unexpected transport type"); 2146 } 2147 2148 assertEquals(evalType, stats.mEvaluationType); 2149 if ((evalType & DATA_STALL_EVALUATION_TYPE_TCP) != 0) { 2150 assertEquals(TEST_TCP_FAIL_RATE, stats.mTcpFailRate); 2151 assertEquals(TEST_TCP_PACKET_COUNT, stats.mTcpSentSinceLastRecv); 2152 } else { 2153 assertEquals(DataStallDetectionStats.UNSPECIFIED_TCP_FAIL_RATE, stats.mTcpFailRate); 2154 assertEquals(DataStallDetectionStats.UNSPECIFIED_TCP_PACKETS_COUNT, 2155 stats.mTcpSentSinceLastRecv); 2156 } 2157 2158 if ((evalType & DATA_STALL_EVALUATION_TYPE_DNS) != 0) { 2159 assertArrayEquals(stats.mDns, makeTestDnsTimeoutNano(DEFAULT_DNS_TIMEOUT_THRESHOLD)); 2160 } else { 2161 assertArrayEquals(stats.mDns, makeTestDnsTimeoutNano(0 /* times */)); 2162 } 2163 } 2164 2165 private DataStallDetectionStats makeTestDataStallDetectionStats(int evaluationType, 2166 int transportType) { 2167 final DataStallDetectionStats.Builder stats = new DataStallDetectionStats.Builder() 2168 .setEvaluationType(evaluationType) 2169 .setNetworkType(transportType); 2170 switch (transportType) { 2171 case NetworkCapabilities.TRANSPORT_CELLULAR: 2172 stats.setCellData(TelephonyManager.NETWORK_TYPE_LTE /* radioType */, 2173 true /* roaming */, 2174 TEST_MCCMNC /* networkMccmnc */, 2175 TEST_MCCMNC /* simMccmnc */, 2176 CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN /* signalStrength */); 2177 break; 2178 case NetworkCapabilities.TRANSPORT_WIFI: 2179 setupTestWifiInfo(); 2180 stats.setWiFiData(mWifiInfo); 2181 break; 2182 default: 2183 break; 2184 } 2185 2186 if ((evaluationType & DATA_STALL_EVALUATION_TYPE_TCP) != 0) { 2187 generateTestTcpStats(stats); 2188 } 2189 2190 if ((evaluationType & DATA_STALL_EVALUATION_TYPE_DNS) != 0) { 2191 generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD); 2192 } 2193 2194 return stats.build(); 2195 } 2196 2197 private byte[] makeTestDnsTimeoutNano(int timeoutCount) { 2198 // Make a expected nano dns message. 2199 final DnsEvent event = new DnsEvent(); 2200 event.dnsReturnCode = new int[timeoutCount]; 2201 event.dnsTime = new long[timeoutCount]; 2202 Arrays.fill(event.dnsReturnCode, RETURN_CODE_DNS_TIMEOUT); 2203 Arrays.fill(event.dnsTime, TEST_ELAPSED_TIME_MS); 2204 return MessageNano.toByteArray(event); 2205 } 2206 2207 private byte[] makeTestCellDataNano() { 2208 final CellularData data = new CellularData(); 2209 data.ratType = DataStallEventProto.RADIO_TECHNOLOGY_LTE; 2210 data.networkMccmnc = TEST_MCCMNC; 2211 data.simMccmnc = TEST_MCCMNC; 2212 data.isRoaming = true; 2213 data.signalStrength = 0; 2214 return MessageNano.toByteArray(data); 2215 } 2216 2217 private byte[] makeTestWifiDataNano() { 2218 final WifiData data = new WifiData(); 2219 data.wifiBand = DataStallEventProto.AP_BAND_2GHZ; 2220 data.signalStrength = TEST_SIGNAL_STRENGTH; 2221 return MessageNano.toByteArray(data); 2222 } 2223 2224 private void setupTestWifiInfo() { 2225 when(mWifi.getConnectionInfo()).thenReturn(mWifiInfo); 2226 when(mWifiInfo.getRssi()).thenReturn(TEST_SIGNAL_STRENGTH); 2227 // Set to 2.4G band. Map to DataStallEventProto.AP_BAND_2GHZ proto definition. 2228 when(mWifiInfo.getFrequency()).thenReturn(2450); 2229 } 2230 2231 private void testDataStallMetricsWithCellular(int evalType) { 2232 testDataStallMetrics(evalType, NetworkCapabilities.TRANSPORT_CELLULAR); 2233 } 2234 2235 private void testDataStallMetricsWithWiFi(int evalType) { 2236 testDataStallMetrics(evalType, NetworkCapabilities.TRANSPORT_WIFI); 2237 } 2238 2239 private void testDataStallMetrics(int evalType, int transportType) { 2240 setDataStallEvaluationType(evalType); 2241 final NetworkCapabilities nc = new NetworkCapabilities() 2242 .addTransportType(transportType) 2243 .addCapability(NET_CAPABILITY_INTERNET); 2244 final WrappedNetworkMonitor wrappedMonitor = makeMonitor(nc); 2245 setupTestWifiInfo(); 2246 final DataStallDetectionStats stats = 2247 makeTestDataStallDetectionStats(evalType, transportType); 2248 assertEquals(wrappedMonitor.buildDataStallDetectionStats(transportType, evalType), stats); 2249 2250 if ((evalType & DATA_STALL_EVALUATION_TYPE_TCP) != 0) { 2251 verify(mTst, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()).getLatestPacketFailPercentage(); 2252 } else { 2253 verify(mTst, never()).getLatestPacketFailPercentage(); 2254 } 2255 } 2256 2257 @Test 2258 public void testCollectDataStallMetrics_DnsWithCellular() { 2259 testDataStallMetricsWithCellular(DATA_STALL_EVALUATION_TYPE_DNS); 2260 } 2261 2262 @Test 2263 public void testCollectDataStallMetrics_DnsWithWiFi() { 2264 testDataStallMetricsWithWiFi(DATA_STALL_EVALUATION_TYPE_DNS); 2265 } 2266 2267 @Test 2268 public void testCollectDataStallMetrics_TcpWithCellular() { 2269 assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)); 2270 testDataStallMetricsWithCellular(DATA_STALL_EVALUATION_TYPE_TCP); 2271 } 2272 2273 @Test 2274 public void testCollectDataStallMetrics_TcpWithWiFi() { 2275 assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)); 2276 testDataStallMetricsWithWiFi(DATA_STALL_EVALUATION_TYPE_TCP); 2277 } 2278 2279 @Test 2280 public void testCollectDataStallMetrics_TcpAndDnsWithWifi() { 2281 assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)); 2282 testDataStallMetricsWithWiFi( 2283 DATA_STALL_EVALUATION_TYPE_TCP | DATA_STALL_EVALUATION_TYPE_DNS); 2284 } 2285 2286 @Test 2287 public void testCollectDataStallMetrics_TcpAndDnsWithCellular() { 2288 assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)); 2289 testDataStallMetricsWithCellular( 2290 DATA_STALL_EVALUATION_TYPE_TCP | DATA_STALL_EVALUATION_TYPE_DNS); 2291 } 2292 2293 @Test 2294 public void testIgnoreHttpsProbe() throws Exception { 2295 setSslException(mHttpsConnection); 2296 setStatus(mHttpConnection, 204); 2297 // Expect to send HTTP, HTTPS, FALLBACK probe and evaluation result notifications to CS. 2298 final NetworkMonitor nm = runNetworkTest(NETWORK_VALIDATION_RESULT_PARTIAL, 2299 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP, 2300 null /* redirectUrl */); 2301 2302 resetCallbacks(); 2303 nm.setAcceptPartialConnectivity(); 2304 // Expect to update evaluation result notifications to CS. 2305 verifyNetworkTested(NETWORK_VALIDATION_RESULT_PARTIAL | NETWORK_VALIDATION_RESULT_VALID, 2306 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP); 2307 } 2308 2309 @Test 2310 public void testIsPartialConnectivity() throws Exception { 2311 setStatus(mHttpsConnection, 500); 2312 setStatus(mHttpConnection, 204); 2313 setStatus(mFallbackConnection, 500); 2314 runPartialConnectivityNetworkTest( 2315 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP); 2316 } 2317 2318 @Test 2319 public void testIsCaptivePortal_OnlyFallbackSucceed() throws Exception { 2320 setStatus(mHttpsConnection, 500); 2321 setStatus(mHttpConnection, 500); 2322 setStatus(mFallbackConnection, 204); 2323 runNetworkTest(VALIDATION_RESULT_INVALID, 2324 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_FALLBACK, 2325 null /* redirectUrl */); 2326 } 2327 2328 private void assertIpAddressArrayEquals(String[] expected, InetAddress[] actual) { 2329 String[] actualStrings = new String[actual.length]; 2330 for (int i = 0; i < actual.length; i++) { 2331 actualStrings[i] = actual[i].getHostAddress(); 2332 } 2333 assertArrayEquals("Array of IP addresses differs", expected, actualStrings); 2334 } 2335 2336 @Test 2337 public void testSendDnsProbeWithTimeout() throws Exception { 2338 WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); 2339 final int shortTimeoutMs = 200; 2340 // v6 only. 2341 String[] expected = new String[]{"2001:db8::"}; 2342 mFakeDns.setAnswer("www.google.com", expected, TYPE_AAAA); 2343 InetAddress[] actual = wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); 2344 assertIpAddressArrayEquals(expected, actual); 2345 // v4 only. 2346 expected = new String[]{"192.0.2.1"}; 2347 mFakeDns.setAnswer("www.android.com", expected, TYPE_A); 2348 actual = wnm.sendDnsProbeWithTimeout("www.android.com", shortTimeoutMs); 2349 assertIpAddressArrayEquals(expected, actual); 2350 // Both v4 & v6. 2351 expected = new String[]{"192.0.2.1", "2001:db8::"}; 2352 mFakeDns.setAnswer("www.googleapis.com", new String[]{"192.0.2.1"}, TYPE_A); 2353 mFakeDns.setAnswer("www.googleapis.com", new String[]{"2001:db8::"}, TYPE_AAAA); 2354 actual = wnm.sendDnsProbeWithTimeout("www.googleapis.com", shortTimeoutMs); 2355 assertIpAddressArrayEquals(expected, actual); 2356 // Clear DNS response. 2357 mFakeDns.setAnswer("www.android.com", new String[0], TYPE_A); 2358 try { 2359 actual = wnm.sendDnsProbeWithTimeout("www.android.com", shortTimeoutMs); 2360 fail("No DNS results, expected UnknownHostException"); 2361 } catch (UnknownHostException e) { 2362 } 2363 2364 mFakeDns.setAnswer("www.android.com", null, TYPE_A); 2365 mFakeDns.setAnswer("www.android.com", null, TYPE_AAAA); 2366 try { 2367 wnm.sendDnsProbeWithTimeout("www.android.com", shortTimeoutMs); 2368 fail("DNS query timed out, expected UnknownHostException"); 2369 } catch (UnknownHostException e) { 2370 } 2371 } 2372 2373 @Test 2374 public void testNotifyNetwork_WithforceReevaluation() throws Exception { 2375 setValidProbes(); 2376 final NetworkMonitor nm = runValidatedNetworkTest(); 2377 // Verify forceReevaluation will not reset the validation result but only probe result until 2378 // getting the validation result. 2379 resetCallbacks(); 2380 setSslException(mHttpsConnection); 2381 setStatus(mHttpConnection, 500); 2382 setStatus(mFallbackConnection, 204); 2383 nm.forceReevaluation(Process.myUid()); 2384 // Expect to send HTTP, HTTPs, FALLBACK and evaluation results. 2385 verifyNetworkTested(VALIDATION_RESULT_INVALID, 2386 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_FALLBACK, 2387 null /* redirectUrl */); 2388 HandlerUtils.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS); 2389 } 2390 2391 @Test 2392 public void testNotifyNetwork_NotifyNetworkTestedOldInterfaceVersion() throws Exception { 2393 // Use old interface version so notifyNetworkTested is used over 2394 // notifyNetworkTestedWithExtras 2395 resetCallbacks(4); 2396 2397 // Trigger Network validation 2398 setStatus(mHttpsConnection, 204); 2399 setStatus(mHttpConnection, 204); 2400 final NetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES); 2401 nm.notifyNetworkConnected(TEST_LINK_PROPERTIES, CELL_METERED_CAPABILITIES); 2402 2403 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)) 2404 .notifyNetworkTested(eq(NETWORK_VALIDATION_RESULT_VALID 2405 | NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS), 2406 eq(null) /* redirectUrl */); 2407 } 2408 2409 @Test 2410 public void testDismissPortalInValidatedNetworkEnabledOsSupported() throws Exception { 2411 assumeTrue(ShimUtils.isAtLeastR()); 2412 testDismissPortalInValidatedNetworkEnabled(TEST_LOGIN_URL, TEST_LOGIN_URL); 2413 } 2414 2415 @Test 2416 public void testDismissPortalInValidatedNetworkEnabledOsSupported_NullLocationUrl() 2417 throws Exception { 2418 assumeTrue(ShimUtils.isAtLeastR()); 2419 testDismissPortalInValidatedNetworkEnabled(TEST_HTTP_URL, null /* locationUrl */); 2420 } 2421 2422 @Test 2423 public void testDismissPortalInValidatedNetworkEnabledOsSupported_InvalidLocationUrl() 2424 throws Exception { 2425 assumeTrue(ShimUtils.isAtLeastR()); 2426 testDismissPortalInValidatedNetworkEnabled(TEST_HTTP_URL, TEST_RELATIVE_URL); 2427 } 2428 2429 @Test 2430 public void testDismissPortalInValidatedNetworkEnabledOsNotSupported() throws Exception { 2431 assumeFalse(ShimUtils.isAtLeastR()); 2432 testDismissPortalInValidatedNetworkEnabled(TEST_HTTP_URL, TEST_LOGIN_URL); 2433 } 2434 2435 private void testDismissPortalInValidatedNetworkEnabled(String expectedUrl, String locationUrl) 2436 throws Exception { 2437 setDismissPortalInValidatedNetwork(true); 2438 setSslException(mHttpsConnection); 2439 setPortal302(mHttpConnection); 2440 when(mHttpConnection.getHeaderField(eq("location"))).thenReturn(locationUrl); 2441 final NetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES); 2442 notifyNetworkConnected(nm, CELL_METERED_CAPABILITIES); 2443 2444 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) 2445 .showProvisioningNotification(any(), any()); 2446 2447 assertEquals(1, mRegisteredReceivers.size()); 2448 // Check that startCaptivePortalApp sends the expected intent. 2449 nm.launchCaptivePortalApp(); 2450 2451 final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); 2452 final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class); 2453 verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1)) 2454 .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture()); 2455 verify(mNotifier).notifyCaptivePortalValidationPending(networkCaptor.getValue()); 2456 final Bundle bundle = bundleCaptor.getValue(); 2457 final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK); 2458 assertEquals(TEST_NETID, bundleNetwork.netId); 2459 // Network is passed both in bundle and as parameter, as the bundle is opaque to the 2460 // framework and only intended for the captive portal app, but the framework needs 2461 // the network to identify the right NetworkMonitor. 2462 assertEquals(TEST_NETID, networkCaptor.getValue().netId); 2463 // Portal URL should be redirect URL. 2464 final String redirectUrl = bundle.getString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL); 2465 assertEquals(expectedUrl, redirectUrl); 2466 } 2467 2468 @Test 2469 public void testEvaluationState_clearProbeResults() throws Exception { 2470 setValidProbes(); 2471 final NetworkMonitor nm = runValidatedNetworkTest(); 2472 nm.getEvaluationState().clearProbeResults(); 2473 // Verify probe results are all reset and only evaluation result left. 2474 assertEquals(NETWORK_VALIDATION_RESULT_VALID, 2475 nm.getEvaluationState().getEvaluationResult()); 2476 assertEquals(0, nm.getEvaluationState().getProbeResults()); 2477 } 2478 2479 @Test 2480 public void testEvaluationState_reportProbeResult() throws Exception { 2481 setValidProbes(); 2482 final NetworkMonitor nm = runValidatedNetworkTest(); 2483 2484 resetCallbacks(); 2485 2486 nm.reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, 2487 CaptivePortalProbeResult.success(1 << PROBE_HTTP)); 2488 // Verify result should be appended and notifyNetworkTestedWithExtras callback is triggered 2489 // once. 2490 assertEquals(NETWORK_VALIDATION_RESULT_VALID, 2491 nm.getEvaluationState().getEvaluationResult()); 2492 assertEquals(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS 2493 | NETWORK_VALIDATION_PROBE_HTTP, nm.getEvaluationState().getProbeResults()); 2494 2495 nm.reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, 2496 CaptivePortalProbeResult.failed(1 << PROBE_HTTP)); 2497 // Verify DNS probe result should not be cleared. 2498 assertEquals(NETWORK_VALIDATION_PROBE_DNS, 2499 nm.getEvaluationState().getProbeResults() & NETWORK_VALIDATION_PROBE_DNS); 2500 } 2501 2502 @Test 2503 public void testEvaluationState_reportEvaluationResult() throws Exception { 2504 setStatus(mHttpsConnection, 500); 2505 setStatus(mHttpConnection, 204); 2506 final NetworkMonitor nm = runNetworkTest(NETWORK_VALIDATION_RESULT_PARTIAL, 2507 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP, 2508 null /* redirectUrl */); 2509 2510 nm.getEvaluationState().reportEvaluationResult(NETWORK_VALIDATION_RESULT_VALID, 2511 null /* redirectUrl */); 2512 verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID, 2513 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP); 2514 2515 nm.getEvaluationState().reportEvaluationResult( 2516 NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_PARTIAL, 2517 null /* redirectUrl */); 2518 verifyNetworkTested( 2519 NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_PARTIAL, 2520 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP); 2521 2522 nm.getEvaluationState().reportEvaluationResult(VALIDATION_RESULT_INVALID, 2523 TEST_REDIRECT_URL); 2524 verifyNetworkTested(VALIDATION_RESULT_INVALID, 2525 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP, 2526 TEST_REDIRECT_URL); 2527 } 2528 2529 @Test 2530 public void testExtractCharset() { 2531 assertEquals(StandardCharsets.UTF_8, extractCharset(null)); 2532 assertEquals(StandardCharsets.UTF_8, extractCharset("text/html;charset=utf-8")); 2533 assertEquals(StandardCharsets.UTF_8, extractCharset("text/html;charset=UtF-8")); 2534 assertEquals(StandardCharsets.UTF_8, extractCharset("text/html; Charset=\"utf-8\"")); 2535 assertEquals(StandardCharsets.UTF_8, extractCharset("image/png")); 2536 assertEquals(StandardCharsets.UTF_8, extractCharset("Text/HTML;")); 2537 assertEquals(StandardCharsets.UTF_8, extractCharset("multipart/form-data; boundary=-aa*-")); 2538 assertEquals(StandardCharsets.UTF_8, extractCharset("text/plain;something=else")); 2539 assertEquals(StandardCharsets.UTF_8, extractCharset("text/plain;charset=ImNotACharset")); 2540 2541 assertEquals(StandardCharsets.ISO_8859_1, extractCharset("text/plain; CharSeT=ISO-8859-1")); 2542 assertEquals(Charset.forName("Shift_JIS"), extractCharset("text/plain;charset=Shift_JIS")); 2543 assertEquals(Charset.forName("Windows-1251"), extractCharset( 2544 "text/plain;charset=Windows-1251 ; somethingelse")); 2545 } 2546 2547 @Test 2548 public void testReadAsString() throws IOException { 2549 final String repeatedString = "1aテスト-?"; 2550 // Infinite stream repeating characters 2551 class TestInputStream extends InputStream { 2552 private final byte[] mBytes = repeatedString.getBytes(StandardCharsets.UTF_8); 2553 private int mPosition = -1; 2554 2555 @Override 2556 public int read() { 2557 mPosition = (mPosition + 1) % mBytes.length; 2558 return mBytes[mPosition]; 2559 } 2560 } 2561 2562 final String readString = NetworkMonitor.readAsString(new TestInputStream(), 2563 1500 /* maxLength */, StandardCharsets.UTF_8); 2564 2565 assertEquals(1500, readString.length()); 2566 for (int i = 0; i < readString.length(); i++) { 2567 assertEquals(repeatedString.charAt(i % repeatedString.length()), readString.charAt(i)); 2568 } 2569 } 2570 2571 @Test 2572 public void testReadAsString_StreamShorterThanLimit() throws Exception { 2573 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); 2574 final byte[] content = "The HTTP response code is 200 but it is not a captive portal." 2575 .getBytes(StandardCharsets.UTF_8); 2576 assertEquals(new String(content), wnm.readAsString(new ByteArrayInputStream(content), 2577 content.length, StandardCharsets.UTF_8)); 2578 // Test the case that the stream ends earlier than the limit. 2579 assertEquals(new String(content), wnm.readAsString(new ByteArrayInputStream(content), 2580 content.length + 10, StandardCharsets.UTF_8)); 2581 } 2582 2583 @Test 2584 public void testMultipleProbesOnPortalNetwork() throws Exception { 2585 setupResourceForMultipleProbes(); 2586 // One of the http probes is portal, then result is portal. 2587 setPortal302(mOtherHttpConnection1); 2588 runPortalNetworkTest(); 2589 // Get conclusive result from one of the HTTP probe. Expect to create 2 HTTP and 2 HTTPS 2590 // probes as resource configuration, but the portal can be detected before other probes 2591 // start. 2592 verify(mCleartextDnsNetwork, atMost(4)).openConnection(any()); 2593 verify(mCleartextDnsNetwork, atLeastOnce()).openConnection(any()); 2594 verify(mOtherHttpConnection1).getResponseCode(); 2595 } 2596 2597 @Test 2598 public void testMultipleProbesOnValidNetwork() throws Exception { 2599 setupResourceForMultipleProbes(); 2600 // One of the https probes succeeds, then it's validated. 2601 setStatus(mOtherHttpsConnection2, 204); 2602 runValidatedNetworkTest(); 2603 // Get conclusive result from one of the HTTPS probe. Expect to create 2 HTTP and 2 HTTPS 2604 // probes as resource configuration, but the network may validate from the HTTPS probe 2605 // before other probes start. 2606 verify(mCleartextDnsNetwork, atMost(4)).openConnection(any()); 2607 verify(mCleartextDnsNetwork, atLeastOnce()).openConnection(any()); 2608 verify(mOtherHttpsConnection2).getResponseCode(); 2609 } 2610 2611 @Test 2612 public void testMultipleProbesOnInValidNetworkForPrioritizedResource() throws Exception { 2613 setupResourceForMultipleProbes(); 2614 // The configuration resource is prioritized. Only use configurations from resource.(i.e 2615 // Only configuration for mOtherHttpsConnection2, mOtherHttpsConnection2, 2616 // mOtherHttpConnection2, mOtherHttpConnection2 will affect the result.) 2617 // Configure mHttpsConnection is no-op. 2618 setStatus(mHttpsConnection, 204); 2619 runFailedNetworkTest(); 2620 // No conclusive result from both HTTP and HTTPS probes. Expect to create 2 HTTP and 2 HTTPS 2621 // probes as resource configuration. All probes are expected to have been run because this 2622 // network is set to never validate (no probe has a success or portal result), so NM tests 2623 // all probes to completion. 2624 verify(mCleartextDnsNetwork, times(4)).openConnection(any()); 2625 verify(mHttpsConnection, never()).getResponseCode(); 2626 } 2627 2628 @Test 2629 public void testMultipleProbesOnInValidNetwork() throws Exception { 2630 setupResourceForMultipleProbes(); 2631 runFailedNetworkTest(); 2632 // No conclusive result from both HTTP and HTTPS probes. Expect to create 2 HTTP and 2 HTTPS 2633 // probes as resource configuration. 2634 verify(mCleartextDnsNetwork, times(4)).openConnection(any()); 2635 } 2636 2637 @Test 2638 public void testIsCaptivePortal_FromExternalSource() throws Exception { 2639 assumeTrue(CaptivePortalDataShimImpl.isSupported()); 2640 assumeTrue(ShimUtils.isAtLeastS()); 2641 when(mDependencies.isFeatureEnabled(any(), eq(NAMESPACE_CONNECTIVITY), 2642 eq(DISMISS_PORTAL_IN_VALIDATED_NETWORK), anyBoolean())).thenReturn(true); 2643 final NetworkMonitor monitor = makeMonitor(WIFI_NOT_METERED_CAPABILITIES); 2644 2645 NetworkInformationShim networkShim = NetworkInformationShimImpl.newInstance(); 2646 CaptivePortalDataShim captivePortalData = new CaptivePortalDataShimImpl( 2647 new CaptivePortalData.Builder().setCaptive(true).build()); 2648 final LinkProperties linkProperties = new LinkProperties(TEST_LINK_PROPERTIES); 2649 networkShim.setCaptivePortalData(linkProperties, captivePortalData); 2650 CaptivePortalDataShim captivePortalDataShim = 2651 networkShim.getCaptivePortalData(linkProperties); 2652 2653 try { 2654 // Set up T&C captive portal info from Passpoint 2655 captivePortalData = captivePortalDataShim.withPasspointInfo(TEST_FRIENDLY_NAME, 2656 Uri.parse(TEST_VENUE_INFO_URL), Uri.parse(TEST_LOGIN_URL)); 2657 } catch (UnsupportedApiLevelException e) { 2658 // Minimum API level for this test is 31 2659 return; 2660 } 2661 2662 networkShim.setCaptivePortalData(linkProperties, captivePortalData); 2663 monitor.notifyLinkPropertiesChanged(linkProperties); 2664 final NetworkCapabilities networkCapabilities = 2665 new NetworkCapabilities(WIFI_NOT_METERED_CAPABILITIES); 2666 monitor.notifyNetworkConnected(linkProperties, networkCapabilities); 2667 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) 2668 .showProvisioningNotification(any(), any()); 2669 assertEquals(1, mRegisteredReceivers.size()); 2670 verifyNetworkTested(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, TEST_LOGIN_URL); 2671 2672 // Force reevaluation and confirm that the network is still captive 2673 HandlerUtils.waitForIdle(monitor.getHandler(), HANDLER_TIMEOUT_MS); 2674 resetCallbacks(); 2675 monitor.forceReevaluation(Process.myUid()); 2676 assertEquals(monitor.getEvaluationState().getProbeCompletedResult(), 0); 2677 verifyNetworkTested(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, TEST_LOGIN_URL); 2678 2679 // Check that startCaptivePortalApp sends the expected intent. 2680 monitor.launchCaptivePortalApp(); 2681 2682 verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1)).startCaptivePortalApp( 2683 argThat(network -> TEST_NETID == network.netId), 2684 argThat(bundle -> bundle.getString( 2685 ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL).equals(TEST_LOGIN_URL) 2686 && TEST_NETID == ((Network) bundle.getParcelable( 2687 ConnectivityManager.EXTRA_NETWORK)).netId)); 2688 } 2689 2690 @Test 2691 public void testOemPaidNetworkValidated() throws Exception { 2692 setValidProbes(); 2693 2694 final NetworkMonitor nm = runNetworkTest(TEST_LINK_PROPERTIES, 2695 WIFI_OEM_PAID_CAPABILITIES, 2696 NETWORK_VALIDATION_RESULT_VALID, 2697 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS, 2698 null /* redirectUrl */); 2699 assertEquals(NETWORK_VALIDATION_RESULT_VALID, 2700 nm.getEvaluationState().getEvaluationResult()); 2701 } 2702 2703 @Test 2704 public void testOemPaidNetwork_AllProbesFailed() throws Exception { 2705 setSslException(mHttpsConnection); 2706 setStatus(mHttpConnection, 500); 2707 setStatus(mFallbackConnection, 404); 2708 2709 runNetworkTest(TEST_LINK_PROPERTIES, 2710 WIFI_OEM_PAID_CAPABILITIES, 2711 VALIDATION_RESULT_INVALID, 0 /* probesSucceeded */, null /* redirectUrl */); 2712 } 2713 2714 @Test 2715 public void testOemPaidNetworkNoInternetCapabilityValidated() throws Exception { 2716 setSslException(mHttpsConnection); 2717 setStatus(mHttpConnection, 500); 2718 setStatus(mFallbackConnection, 404); 2719 2720 final NetworkCapabilities networkCapabilities = 2721 new NetworkCapabilities(WIFI_OEM_PAID_CAPABILITIES); 2722 networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET); 2723 runNetworkTest(TEST_LINK_PROPERTIES, networkCapabilities, 2724 NETWORK_VALIDATION_RESULT_VALID, 0 /* probesSucceeded */, null /* redirectUrl */); 2725 2726 verify(mCleartextDnsNetwork, never()).openConnection(any()); 2727 verify(mHttpsConnection, never()).getResponseCode(); 2728 verify(mHttpConnection, never()).getResponseCode(); 2729 verify(mFallbackConnection, never()).getResponseCode(); 2730 } 2731 2732 @Test 2733 public void testOemPaidNetwork_CaptivePortalNotLaunched() throws Exception { 2734 setSslException(mHttpsConnection); 2735 setStatus(mFallbackConnection, 404); 2736 setPortal302(mHttpConnection); 2737 2738 runNetworkTest(TEST_LINK_PROPERTIES, WIFI_OEM_PAID_CAPABILITIES, 2739 VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, 2740 TEST_LOGIN_URL); 2741 2742 verify(mCallbacks, never()).showProvisioningNotification(any(), any()); 2743 } 2744 2745 private void setupResourceForMultipleProbes() { 2746 // Configure the resource to send multiple probe. 2747 when(mResources.getStringArray(R.array.config_captive_portal_https_urls)) 2748 .thenReturn(TEST_HTTPS_URLS); 2749 when(mResources.getStringArray(R.array.config_captive_portal_http_urls)) 2750 .thenReturn(TEST_HTTP_URLS); 2751 } 2752 2753 private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) { 2754 for (int i = 0; i < count; i++) { 2755 wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount( 2756 RETURN_CODE_DNS_TIMEOUT); 2757 } 2758 } 2759 2760 private void makeDnsSuccessEvent(WrappedNetworkMonitor wrappedMonitor, int count) { 2761 for (int i = 0; i < count; i++) { 2762 wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount( 2763 RETURN_CODE_DNS_SUCCESS); 2764 } 2765 } 2766 2767 private DataStallDetectionStats makeEmptyDataStallDetectionStats() { 2768 return new DataStallDetectionStats.Builder().build(); 2769 } 2770 2771 private void setDataStallEvaluationType(int type) { 2772 when(mDependencies.getDeviceConfigPropertyInt(any(), 2773 eq(CONFIG_DATA_STALL_EVALUATION_TYPE), anyInt())).thenReturn(type); 2774 } 2775 2776 private void setMinDataStallEvaluateInterval(int time) { 2777 when(mDependencies.getDeviceConfigPropertyInt(any(), 2778 eq(CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL), anyInt())).thenReturn(time); 2779 } 2780 2781 private void setValidDataStallDnsTimeThreshold(int time) { 2782 when(mDependencies.getDeviceConfigPropertyInt(any(), 2783 eq(CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD), anyInt())).thenReturn(time); 2784 } 2785 2786 private void setConsecutiveDnsTimeoutThreshold(int num) { 2787 when(mDependencies.getDeviceConfigPropertyInt(any(), 2788 eq(CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD), anyInt())).thenReturn(num); 2789 } 2790 2791 private void setTcpPollingInterval(int time) { 2792 doReturn(time).when(mDependencies).getDeviceConfigPropertyInt(any(), 2793 eq(CONFIG_DATA_STALL_TCP_POLLING_INTERVAL), anyInt()); 2794 } 2795 2796 private void setFallbackUrl(String url) { 2797 when(mDependencies.getSetting(any(), 2798 eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL), any())).thenReturn(url); 2799 } 2800 2801 private void setOtherFallbackUrls(String urls) { 2802 when(mDependencies.getDeviceConfigProperty(any(), 2803 eq(CAPTIVE_PORTAL_OTHER_FALLBACK_URLS), any())).thenReturn(urls); 2804 } 2805 2806 private void setFallbackSpecs(String specs) { 2807 when(mDependencies.getDeviceConfigProperty(any(), 2808 eq(CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs); 2809 } 2810 2811 private void setCaptivePortalMode(int mode) { 2812 when(mDependencies.getSetting(any(), 2813 eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())).thenReturn(mode); 2814 } 2815 2816 private void setDismissPortalInValidatedNetwork(boolean enabled) { 2817 when(mDependencies.isFeatureEnabled(any(), any(), 2818 eq(DISMISS_PORTAL_IN_VALIDATED_NETWORK), anyBoolean())).thenReturn(enabled); 2819 } 2820 2821 private void setDeviceConfig(String key, String value) { 2822 doReturn(value).when(mDependencies).getDeviceConfigProperty(eq(NAMESPACE_CONNECTIVITY), 2823 eq(key), any() /* defaultValue */); 2824 } 2825 2826 private NetworkMonitor runPortalNetworkTest() throws RemoteException { 2827 final NetworkMonitor nm = runNetworkTest(VALIDATION_RESULT_PORTAL, 2828 0 /* probesSucceeded */, TEST_LOGIN_URL); 2829 assertEquals(1, mRegisteredReceivers.size()); 2830 return nm; 2831 } 2832 2833 private NetworkMonitor runNoValidationNetworkTest() throws RemoteException { 2834 final NetworkMonitor nm = runNetworkTest(NETWORK_VALIDATION_RESULT_VALID, 2835 0 /* probesSucceeded */, null /* redirectUrl */); 2836 assertEquals(0, mRegisteredReceivers.size()); 2837 return nm; 2838 } 2839 2840 private NetworkMonitor runFailedNetworkTest() throws RemoteException { 2841 final NetworkMonitor nm = runNetworkTest( 2842 VALIDATION_RESULT_INVALID, 0 /* probesSucceeded */, null /* redirectUrl */); 2843 assertEquals(0, mRegisteredReceivers.size()); 2844 return nm; 2845 } 2846 2847 private NetworkMonitor runPartialConnectivityNetworkTest(int probesSucceeded) 2848 throws RemoteException { 2849 final NetworkMonitor nm = runNetworkTest(NETWORK_VALIDATION_RESULT_PARTIAL, 2850 probesSucceeded, null /* redirectUrl */); 2851 assertEquals(0, mRegisteredReceivers.size()); 2852 return nm; 2853 } 2854 2855 private NetworkMonitor runValidatedNetworkTest() throws RemoteException { 2856 // Expect to send HTTPS and evaluation results. 2857 return runNetworkTest(NETWORK_VALIDATION_RESULT_VALID, 2858 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS, 2859 null /* redirectUrl */); 2860 } 2861 2862 private NetworkMonitor runNetworkTest(int testResult, int probesSucceeded, String redirectUrl) 2863 throws RemoteException { 2864 return runNetworkTest(TEST_LINK_PROPERTIES, CELL_METERED_CAPABILITIES, testResult, 2865 probesSucceeded, redirectUrl); 2866 } 2867 2868 private NetworkMonitor runNetworkTest(LinkProperties lp, NetworkCapabilities nc, 2869 int testResult, int probesSucceeded, String redirectUrl) throws RemoteException { 2870 final NetworkMonitor monitor = makeMonitor(nc); 2871 monitor.notifyNetworkConnected(lp, nc); 2872 verifyNetworkTested(testResult, probesSucceeded, redirectUrl); 2873 HandlerUtils.waitForIdle(monitor.getHandler(), HANDLER_TIMEOUT_MS); 2874 2875 return monitor; 2876 } 2877 2878 private void verifyNetworkTested(int testResult, int probesSucceeded) throws RemoteException { 2879 verifyNetworkTested(testResult, probesSucceeded, null /* redirectUrl */); 2880 } 2881 2882 private void verifyNetworkTested(int testResult, int probesSucceeded, String redirectUrl) 2883 throws RemoteException { 2884 try { 2885 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)).notifyNetworkTestedWithExtras( 2886 matchNetworkTestResultParcelable(testResult, probesSucceeded, redirectUrl)); 2887 } catch (AssertionFailedError e) { 2888 // Capture the callbacks up to now to give a better error message 2889 final ArgumentCaptor<NetworkTestResultParcelable> captor = 2890 ArgumentCaptor.forClass(NetworkTestResultParcelable.class); 2891 2892 // Call verify() again to verify the same method call verified by the previous verify 2893 // call which failed, but this time use a captor to log the exact parcel sent by 2894 // NetworkMonitor. 2895 // This assertion will fail if notifyNetworkTested was not called at all. 2896 verify(mCallbacks).notifyNetworkTestedWithExtras(captor.capture()); 2897 2898 final NetworkTestResultParcelable lastResult = captor.getValue(); 2899 fail(String.format("notifyNetworkTestedWithExtras was not called with the " 2900 + "expected result within timeout. " 2901 + "Expected result %d, probes succeeded %d, redirect URL %s, " 2902 + "last result was (%d, %d, %s).", 2903 testResult, probesSucceeded, redirectUrl, 2904 lastResult.result, lastResult.probesSucceeded, lastResult.redirectUrl)); 2905 } 2906 } 2907 2908 private void notifyNetworkConnected(NetworkMonitor nm, NetworkCapabilities nc) { 2909 nm.notifyNetworkConnected(TEST_LINK_PROPERTIES, nc); 2910 } 2911 2912 private void setSslException(HttpURLConnection connection) throws IOException { 2913 doThrow(new SSLHandshakeException("Invalid cert")).when(connection).getResponseCode(); 2914 } 2915 2916 private void setValidProbes() throws IOException { 2917 setStatus(mHttpsConnection, 204); 2918 setStatus(mHttpConnection, 204); 2919 } 2920 2921 private void set302(HttpURLConnection connection, String location) throws IOException { 2922 setStatus(connection, 302); 2923 doReturn(location).when(connection).getHeaderField(LOCATION_HEADER); 2924 } 2925 2926 private void setPortal302(HttpURLConnection connection) throws IOException { 2927 set302(connection, TEST_LOGIN_URL); 2928 } 2929 2930 private void setApiContent(HttpURLConnection connection, String content) throws IOException { 2931 setStatus(connection, 200); 2932 final Map<String, List<String>> headerFields = new HashMap<>(); 2933 headerFields.put( 2934 CONTENT_TYPE_HEADER, singletonList("application/captive+json;charset=UTF-8")); 2935 doReturn(headerFields).when(connection).getHeaderFields(); 2936 doReturn(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))) 2937 .when(connection).getInputStream(); 2938 } 2939 2940 private void setStatus(HttpURLConnection connection, int status) throws IOException { 2941 doReturn(status).when(connection).getResponseCode(); 2942 } 2943 2944 private void generateTimeoutDnsEvent(DataStallDetectionStats.Builder stats, int num) { 2945 for (int i = 0; i < num; i++) { 2946 stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, TEST_ELAPSED_TIME_MS /* timeMs */); 2947 } 2948 } 2949 2950 private void generateTestTcpStats(DataStallDetectionStats.Builder stats) { 2951 when(mTst.getLatestPacketFailPercentage()).thenReturn(TEST_TCP_FAIL_RATE); 2952 when(mTst.getSentSinceLastRecv()).thenReturn(TEST_TCP_PACKET_COUNT); 2953 stats.setTcpFailRate(TEST_TCP_FAIL_RATE).setTcpSentSinceLastRecv(TEST_TCP_PACKET_COUNT); 2954 } 2955 2956 private NetworkTestResultParcelable matchNetworkTestResultParcelable(final int result, 2957 final int probesSucceeded) { 2958 return matchNetworkTestResultParcelable(result, probesSucceeded, null /* redirectUrl */); 2959 } 2960 2961 private NetworkTestResultParcelable matchNetworkTestResultParcelable(final int result, 2962 final int probesSucceeded, String redirectUrl) { 2963 // TODO: also verify probesAttempted 2964 return argThat(p -> p.result == result && p.probesSucceeded == probesSucceeded 2965 && Objects.equals(p.redirectUrl, redirectUrl)); 2966 } 2967 2968 private DataStallReportParcelable matchDnsAndTcpDataStallParcelable(final int timeoutCount) { 2969 return argThat(p -> 2970 (p.detectionMethod & ConstantsShim.DETECTION_METHOD_DNS_EVENTS) != 0 2971 && (p.detectionMethod & ConstantsShim.DETECTION_METHOD_TCP_METRICS) != 0 2972 && p.dnsConsecutiveTimeouts == timeoutCount); 2973 } 2974 2975 private DataStallReportParcelable matchDnsDataStallParcelable(final int timeoutCount) { 2976 return argThat(p -> (p.detectionMethod & ConstantsShim.DETECTION_METHOD_DNS_EVENTS) != 0 2977 && p.dnsConsecutiveTimeouts == timeoutCount); 2978 } 2979 2980 private DataStallReportParcelable matchTcpDataStallParcelable() { 2981 return argThat(p -> (p.detectionMethod & ConstantsShim.DETECTION_METHOD_TCP_METRICS) != 0); 2982 } 2983 } 2984 2985