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.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS; 21 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK; 22 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP; 23 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS; 24 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS; 25 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; 26 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; 27 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 28 import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD; 29 import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_EVALUATION_TYPE; 30 import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL; 31 import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD; 32 import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS; 33 import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS; 34 import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS; 35 import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS; 36 37 import static junit.framework.Assert.assertEquals; 38 import static junit.framework.Assert.assertFalse; 39 40 import static org.junit.Assert.assertArrayEquals; 41 import static org.junit.Assert.assertNotNull; 42 import static org.junit.Assert.assertNull; 43 import static org.junit.Assert.assertTrue; 44 import static org.junit.Assert.fail; 45 import static org.mockito.ArgumentMatchers.eq; 46 import static org.mockito.Mockito.any; 47 import static org.mockito.Mockito.anyInt; 48 import static org.mockito.Mockito.doAnswer; 49 import static org.mockito.Mockito.doReturn; 50 import static org.mockito.Mockito.doThrow; 51 import static org.mockito.Mockito.never; 52 import static org.mockito.Mockito.reset; 53 import static org.mockito.Mockito.timeout; 54 import static org.mockito.Mockito.times; 55 import static org.mockito.Mockito.verify; 56 import static org.mockito.Mockito.when; 57 58 import android.annotation.NonNull; 59 import android.content.BroadcastReceiver; 60 import android.content.Context; 61 import android.content.Intent; 62 import android.content.res.Resources; 63 import android.net.ConnectivityManager; 64 import android.net.DnsResolver; 65 import android.net.INetworkMonitorCallbacks; 66 import android.net.InetAddresses; 67 import android.net.LinkProperties; 68 import android.net.Network; 69 import android.net.NetworkCapabilities; 70 import android.net.NetworkInfo; 71 import android.net.captiveportal.CaptivePortalProbeResult; 72 import android.net.metrics.IpConnectivityLog; 73 import android.net.shared.PrivateDnsConfig; 74 import android.net.util.SharedLog; 75 import android.net.wifi.WifiInfo; 76 import android.net.wifi.WifiManager; 77 import android.os.Bundle; 78 import android.os.ConditionVariable; 79 import android.os.Handler; 80 import android.os.Looper; 81 import android.os.Process; 82 import android.os.RemoteException; 83 import android.os.SystemClock; 84 import android.provider.Settings; 85 import android.telephony.CellSignalStrength; 86 import android.telephony.TelephonyManager; 87 import android.util.ArrayMap; 88 89 import androidx.test.filters.SmallTest; 90 import androidx.test.runner.AndroidJUnit4; 91 92 import com.android.networkstack.R; 93 import com.android.networkstack.metrics.DataStallDetectionStats; 94 import com.android.networkstack.metrics.DataStallStatsUtils; 95 96 import org.junit.After; 97 import org.junit.Before; 98 import org.junit.Test; 99 import org.junit.runner.RunWith; 100 import org.mockito.ArgumentCaptor; 101 import org.mockito.Captor; 102 import org.mockito.Mock; 103 import org.mockito.MockitoAnnotations; 104 import org.mockito.Spy; 105 import org.mockito.verification.VerificationWithTimeout; 106 107 import java.io.IOException; 108 import java.net.HttpURLConnection; 109 import java.net.InetAddress; 110 import java.net.URL; 111 import java.net.UnknownHostException; 112 import java.util.ArrayList; 113 import java.util.HashSet; 114 import java.util.List; 115 import java.util.Random; 116 import java.util.concurrent.Executor; 117 118 import javax.net.ssl.SSLHandshakeException; 119 120 @RunWith(AndroidJUnit4.class) 121 @SmallTest 122 public class NetworkMonitorTest { 123 private static final String LOCATION_HEADER = "location"; 124 125 private @Mock Context mContext; 126 private @Mock Resources mResources; 127 private @Mock IpConnectivityLog mLogger; 128 private @Mock SharedLog mValidationLogger; 129 private @Mock NetworkInfo mNetworkInfo; 130 private @Mock DnsResolver mDnsResolver; 131 private @Mock ConnectivityManager mCm; 132 private @Mock TelephonyManager mTelephony; 133 private @Mock WifiManager mWifi; 134 private @Mock HttpURLConnection mHttpConnection; 135 private @Mock HttpURLConnection mHttpsConnection; 136 private @Mock HttpURLConnection mFallbackConnection; 137 private @Mock HttpURLConnection mOtherFallbackConnection; 138 private @Mock Random mRandom; 139 private @Mock NetworkMonitor.Dependencies mDependencies; 140 private @Mock INetworkMonitorCallbacks mCallbacks; 141 private @Spy Network mCleartextDnsNetwork = new Network(TEST_NETID); 142 private @Mock Network mNetwork; 143 private @Mock DataStallStatsUtils mDataStallStatsUtils; 144 private @Mock WifiInfo mWifiInfo; 145 private @Captor ArgumentCaptor<String> mNetworkTestedRedirectUrlCaptor; 146 147 private HashSet<WrappedNetworkMonitor> mCreatedNetworkMonitors; 148 private HashSet<BroadcastReceiver> mRegisteredReceivers; 149 150 private static final int TEST_NETID = 4242; 151 private static final String TEST_HTTP_URL = "http://www.google.com/gen_204"; 152 private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204"; 153 private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204"; 154 private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204"; 155 private static final String TEST_MCCMNC = "123456"; 156 157 private static final int VALIDATION_RESULT_INVALID = 0; 158 private static final int VALIDATION_RESULT_PORTAL = 0; 159 private static final String TEST_REDIRECT_URL = "android.com"; 160 private static final int VALIDATION_RESULT_PARTIAL = NETWORK_VALIDATION_PROBE_DNS 161 | NETWORK_VALIDATION_PROBE_HTTP 162 | NETWORK_VALIDATION_RESULT_PARTIAL; 163 private static final int VALIDATION_RESULT_FALLBACK_PARTIAL = NETWORK_VALIDATION_PROBE_DNS 164 | NETWORK_VALIDATION_PROBE_FALLBACK 165 | NETWORK_VALIDATION_RESULT_PARTIAL; 166 private static final int VALIDATION_RESULT_VALID = NETWORK_VALIDATION_PROBE_DNS 167 | NETWORK_VALIDATION_PROBE_HTTPS 168 | NETWORK_VALIDATION_RESULT_VALID; 169 170 private static final int RETURN_CODE_DNS_SUCCESS = 0; 171 private static final int RETURN_CODE_DNS_TIMEOUT = 255; 172 private static final int DEFAULT_DNS_TIMEOUT_THRESHOLD = 5; 173 174 private static final int HANDLER_TIMEOUT_MS = 1000; 175 176 private static final LinkProperties TEST_LINK_PROPERTIES = new LinkProperties(); 177 178 private static final NetworkCapabilities METERED_CAPABILITIES = new NetworkCapabilities() 179 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) 180 .addCapability(NET_CAPABILITY_INTERNET); 181 182 private static final NetworkCapabilities NOT_METERED_CAPABILITIES = new NetworkCapabilities() 183 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) 184 .addCapability(NET_CAPABILITY_INTERNET) 185 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); 186 187 private static final NetworkCapabilities NO_INTERNET_CAPABILITIES = new NetworkCapabilities() 188 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); 189 190 /** 191 * Fakes DNS responses. 192 * 193 * Allows test methods to configure the IP addresses that will be resolved by 194 * Network#getAllByName and by DnsResolver#query. 195 */ 196 class FakeDns { 197 private final ArrayMap<String, List<InetAddress>> mAnswers = new ArrayMap<>(); 198 private boolean mNonBypassPrivateDnsWorking = true; 199 200 /** Whether DNS queries on mNonBypassPrivateDnsWorking should succeed. */ setNonBypassPrivateDnsWorking(boolean working)201 private void setNonBypassPrivateDnsWorking(boolean working) { 202 mNonBypassPrivateDnsWorking = working; 203 } 204 205 /** Clears all DNS entries. */ clearAll()206 private synchronized void clearAll() { 207 mAnswers.clear(); 208 } 209 210 /** Returns the answer for a given name on the given mock network. */ getAnswer(Object mock, String hostname)211 private synchronized List<InetAddress> getAnswer(Object mock, String hostname) { 212 if (mock == mNetwork && !mNonBypassPrivateDnsWorking) { 213 return null; 214 } 215 if (mAnswers.containsKey(hostname)) { 216 return mAnswers.get(hostname); 217 } 218 return mAnswers.get("*"); 219 } 220 221 /** Sets the answer for a given name. */ setAnswer(String hostname, String[] answer)222 private synchronized void setAnswer(String hostname, String[] answer) 223 throws UnknownHostException { 224 if (answer == null) { 225 mAnswers.remove(hostname); 226 } else { 227 List<InetAddress> answerList = new ArrayList<>(); 228 for (String addr : answer) { 229 answerList.add(InetAddresses.parseNumericAddress(addr)); 230 } 231 mAnswers.put(hostname, answerList); 232 } 233 } 234 235 /** Simulates a getAllByName call for the specified name on the specified mock network. */ getAllByName(Object mock, String hostname)236 private InetAddress[] getAllByName(Object mock, String hostname) 237 throws UnknownHostException { 238 List<InetAddress> answer = getAnswer(mock, hostname); 239 if (answer == null || answer.size() == 0) { 240 throw new UnknownHostException(hostname); 241 } 242 return answer.toArray(new InetAddress[0]); 243 } 244 245 /** Starts mocking DNS queries. */ startMocking()246 private void startMocking() throws UnknownHostException { 247 // Queries on mNetwork using getAllByName. 248 doAnswer(invocation -> { 249 return getAllByName(invocation.getMock(), invocation.getArgument(0)); 250 }).when(mNetwork).getAllByName(any()); 251 252 // Queries on mCleartextDnsNetwork using DnsResolver#query. 253 doAnswer(invocation -> { 254 String hostname = (String) invocation.getArgument(1); 255 Executor executor = (Executor) invocation.getArgument(3); 256 DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(5); 257 258 List<InetAddress> answer = getAnswer(invocation.getMock(), hostname); 259 if (answer != null && answer.size() > 0) { 260 new Handler(Looper.getMainLooper()).post(() -> { 261 executor.execute(() -> callback.onAnswer(answer, 0)); 262 }); 263 } 264 // If no answers, do nothing. sendDnsProbeWithTimeout will time out and throw UHE. 265 return null; 266 }).when(mDnsResolver).query(any(), any(), anyInt(), any(), any(), any()); 267 268 // Queries on mCleartextDnsNetwork using using DnsResolver#query with QueryType. 269 doAnswer(invocation -> { 270 String hostname = (String) invocation.getArgument(1); 271 Executor executor = (Executor) invocation.getArgument(4); 272 DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(6); 273 274 List<InetAddress> answer = getAnswer(invocation.getMock(), hostname); 275 if (answer != null && answer.size() > 0) { 276 new Handler(Looper.getMainLooper()).post(() -> { 277 executor.execute(() -> callback.onAnswer(answer, 0)); 278 }); 279 } 280 // If no answers, do nothing. sendDnsProbeWithTimeout will time out and throw UHE. 281 return null; 282 }).when(mDnsResolver).query(any(), any(), anyInt(), anyInt(), any(), any(), any()); 283 } 284 } 285 286 private FakeDns mFakeDns; 287 288 @Before setUp()289 public void setUp() throws Exception { 290 MockitoAnnotations.initMocks(this); 291 when(mDependencies.getPrivateDnsBypassNetwork(any())).thenReturn(mCleartextDnsNetwork); 292 when(mDependencies.getDnsResolver()).thenReturn(mDnsResolver); 293 when(mDependencies.getRandom()).thenReturn(mRandom); 294 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())) 295 .thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); 296 when(mDependencies.getDeviceConfigPropertyInt(any(), eq(CAPTIVE_PORTAL_USE_HTTPS), 297 anyInt())).thenReturn(1); 298 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any())) 299 .thenReturn(TEST_HTTP_URL); 300 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL), any())) 301 .thenReturn(TEST_HTTPS_URL); 302 303 doReturn(mCleartextDnsNetwork).when(mNetwork).getPrivateDnsBypassingCopy(); 304 305 when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mCm); 306 when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephony); 307 when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifi); 308 when(mContext.getResources()).thenReturn(mResources); 309 310 when(mResources.getString(anyInt())).thenReturn(""); 311 when(mResources.getStringArray(anyInt())).thenReturn(new String[0]); 312 313 when(mNetworkInfo.getType()).thenReturn(ConnectivityManager.TYPE_WIFI); 314 setFallbackUrl(TEST_FALLBACK_URL); 315 setOtherFallbackUrls(TEST_OTHER_FALLBACK_URL); 316 setFallbackSpecs(null); // Test with no fallback spec by default 317 when(mRandom.nextInt()).thenReturn(0); 318 319 // DNS probe timeout should not be defined more than half of HANDLER_TIMEOUT_MS. Otherwise, 320 // it will fail the test because of timeout expired for querying AAAA and A sequentially. 321 when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout))) 322 .thenReturn(200); 323 324 doAnswer((invocation) -> { 325 URL url = invocation.getArgument(0); 326 switch(url.toString()) { 327 case TEST_HTTP_URL: 328 return mHttpConnection; 329 case TEST_HTTPS_URL: 330 return mHttpsConnection; 331 case TEST_FALLBACK_URL: 332 return mFallbackConnection; 333 case TEST_OTHER_FALLBACK_URL: 334 return mOtherFallbackConnection; 335 default: 336 fail("URL not mocked: " + url.toString()); 337 return null; 338 } 339 }).when(mCleartextDnsNetwork).openConnection(any()); 340 when(mHttpConnection.getRequestProperties()).thenReturn(new ArrayMap<>()); 341 when(mHttpsConnection.getRequestProperties()).thenReturn(new ArrayMap<>()); 342 343 mFakeDns = new FakeDns(); 344 mFakeDns.startMocking(); 345 mFakeDns.setAnswer("*", new String[]{"2001:db8::1", "192.0.2.2"}); 346 347 when(mContext.registerReceiver(any(BroadcastReceiver.class), any())).then((invocation) -> { 348 mRegisteredReceivers.add(invocation.getArgument(0)); 349 return new Intent(); 350 }); 351 352 doAnswer((invocation) -> { 353 mRegisteredReceivers.remove(invocation.getArgument(0)); 354 return null; 355 }).when(mContext).unregisterReceiver(any()); 356 357 resetCallbacks(); 358 359 setMinDataStallEvaluateInterval(500); 360 setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS); 361 setValidDataStallDnsTimeThreshold(500); 362 setConsecutiveDnsTimeoutThreshold(5); 363 364 mCreatedNetworkMonitors = new HashSet<>(); 365 mRegisteredReceivers = new HashSet<>(); 366 } 367 368 @After tearDown()369 public void tearDown() { 370 mFakeDns.clearAll(); 371 assertTrue(mCreatedNetworkMonitors.size() > 0); 372 // Make a local copy of mCreatedNetworkMonitors because during the iteration below, 373 // WrappedNetworkMonitor#onQuitting will delete elements from it on the handler threads. 374 WrappedNetworkMonitor[] networkMonitors = mCreatedNetworkMonitors.toArray( 375 new WrappedNetworkMonitor[0]); 376 for (WrappedNetworkMonitor nm : networkMonitors) { 377 nm.notifyNetworkDisconnected(); 378 nm.awaitQuit(); 379 } 380 assertEquals("NetworkMonitor still running after disconnect", 381 0, mCreatedNetworkMonitors.size()); 382 assertEquals("BroadcastReceiver still registered after disconnect", 383 0, mRegisteredReceivers.size()); 384 } 385 resetCallbacks()386 private void resetCallbacks() { 387 reset(mCallbacks); 388 // TODO: make this a parameterized test. 389 try { 390 when(mCallbacks.getInterfaceVersion()).thenReturn(3); 391 } catch (RemoteException e) { 392 // Can't happen as mCallbacks is a mock 393 fail("Error mocking getInterfaceVersion" + e); 394 } 395 } 396 397 private class WrappedNetworkMonitor extends NetworkMonitor { 398 private long mProbeTime = 0; 399 private final ConditionVariable mQuitCv = new ConditionVariable(false); 400 WrappedNetworkMonitor()401 WrappedNetworkMonitor() { 402 super(mContext, mCallbacks, mNetwork, mLogger, mValidationLogger, 403 mDependencies, mDataStallStatsUtils); 404 } 405 406 @Override getLastProbeTime()407 protected long getLastProbeTime() { 408 return mProbeTime; 409 } 410 setLastProbeTime(long time)411 protected void setLastProbeTime(long time) { 412 mProbeTime = time; 413 } 414 415 @Override addDnsEvents(@onNull final DataStallDetectionStats.Builder stats)416 protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) { 417 generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD); 418 } 419 420 @Override onQuitting()421 protected void onQuitting() { 422 assertTrue(mCreatedNetworkMonitors.remove(this)); 423 mQuitCv.open(); 424 } 425 awaitQuit()426 protected void awaitQuit() { 427 assertTrue("NetworkMonitor did not quit after " + HANDLER_TIMEOUT_MS + "ms", 428 mQuitCv.block(HANDLER_TIMEOUT_MS)); 429 } 430 } 431 makeMonitor(NetworkCapabilities nc)432 private WrappedNetworkMonitor makeMonitor(NetworkCapabilities nc) { 433 final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(); 434 nm.start(); 435 setNetworkCapabilities(nm, nc); 436 waitForIdle(nm.getHandler()); 437 mCreatedNetworkMonitors.add(nm); 438 return nm; 439 } 440 makeMeteredNetworkMonitor()441 private WrappedNetworkMonitor makeMeteredNetworkMonitor() { 442 final WrappedNetworkMonitor nm = makeMonitor(METERED_CAPABILITIES); 443 return nm; 444 } 445 makeNotMeteredNetworkMonitor()446 private WrappedNetworkMonitor makeNotMeteredNetworkMonitor() { 447 final WrappedNetworkMonitor nm = makeMonitor(NOT_METERED_CAPABILITIES); 448 return nm; 449 } 450 setNetworkCapabilities(NetworkMonitor nm, NetworkCapabilities nc)451 private void setNetworkCapabilities(NetworkMonitor nm, NetworkCapabilities nc) { 452 nm.notifyNetworkCapabilitiesChanged(nc); 453 waitForIdle(nm.getHandler()); 454 } 455 waitForIdle(Handler handler)456 private void waitForIdle(Handler handler) { 457 final ConditionVariable cv = new ConditionVariable(false); 458 handler.post(cv::open); 459 if (!cv.block(HANDLER_TIMEOUT_MS)) { 460 fail("Timed out waiting for handler"); 461 } 462 } 463 464 @Test testGetIntSetting()465 public void testGetIntSetting() throws Exception { 466 WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); 467 468 // No config resource, no device config. Expect to get default resource. 469 doThrow(new Resources.NotFoundException()) 470 .when(mResources).getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout)); 471 doAnswer(invocation -> { 472 int defaultValue = invocation.getArgument(2); 473 return defaultValue; 474 }).when(mDependencies).getDeviceConfigPropertyInt(any(), 475 eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT), 476 anyInt()); 477 when(mResources.getInteger(eq(R.integer.default_captive_portal_dns_probe_timeout))) 478 .thenReturn(42); 479 assertEquals(42, wnm.getIntSetting(mContext, 480 R.integer.config_captive_portal_dns_probe_timeout, 481 NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, 482 R.integer.default_captive_portal_dns_probe_timeout)); 483 484 // Set device config. Expect to get device config. 485 when(mDependencies.getDeviceConfigPropertyInt(any(), 486 eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT), anyInt())) 487 .thenReturn(1234); 488 assertEquals(1234, wnm.getIntSetting(mContext, 489 R.integer.config_captive_portal_dns_probe_timeout, 490 NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, 491 R.integer.default_captive_portal_dns_probe_timeout)); 492 493 // Set config resource. Expect to get config resource. 494 when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout))) 495 .thenReturn(5678); 496 assertEquals(5678, wnm.getIntSetting(mContext, 497 R.integer.config_captive_portal_dns_probe_timeout, 498 NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, 499 R.integer.default_captive_portal_dns_probe_timeout)); 500 } 501 502 @Test testIsCaptivePortal_HttpProbeIsPortal()503 public void testIsCaptivePortal_HttpProbeIsPortal() throws IOException { 504 setSslException(mHttpsConnection); 505 setPortal302(mHttpConnection); 506 runPortalNetworkTest(VALIDATION_RESULT_PORTAL); 507 } 508 509 @Test testIsCaptivePortal_HttpsProbeIsNotPortal()510 public void testIsCaptivePortal_HttpsProbeIsNotPortal() throws IOException { 511 setStatus(mHttpsConnection, 204); 512 setStatus(mHttpConnection, 500); 513 514 runNotPortalNetworkTest(); 515 } 516 517 @Test testIsCaptivePortal_FallbackProbeIsPortal()518 public void testIsCaptivePortal_FallbackProbeIsPortal() throws IOException { 519 setSslException(mHttpsConnection); 520 setStatus(mHttpConnection, 500); 521 setPortal302(mFallbackConnection); 522 runPortalNetworkTest(VALIDATION_RESULT_INVALID); 523 } 524 525 @Test testIsCaptivePortal_FallbackProbeIsNotPortal()526 public void testIsCaptivePortal_FallbackProbeIsNotPortal() throws IOException { 527 setSslException(mHttpsConnection); 528 setStatus(mHttpConnection, 500); 529 setStatus(mFallbackConnection, 500); 530 531 // Fallback probe did not see portal, HTTPS failed -> inconclusive 532 runFailedNetworkTest(); 533 } 534 535 @Test testIsCaptivePortal_OtherFallbackProbeIsPortal()536 public void testIsCaptivePortal_OtherFallbackProbeIsPortal() throws IOException { 537 // Set all fallback probes but one to invalid URLs to verify they are being skipped 538 setFallbackUrl(TEST_FALLBACK_URL); 539 setOtherFallbackUrls(TEST_FALLBACK_URL + "," + TEST_OTHER_FALLBACK_URL); 540 541 setSslException(mHttpsConnection); 542 setStatus(mHttpConnection, 500); 543 setStatus(mFallbackConnection, 500); 544 setPortal302(mOtherFallbackConnection); 545 546 // TEST_OTHER_FALLBACK_URL is third 547 when(mRandom.nextInt()).thenReturn(2); 548 549 // First check always uses the first fallback URL: inconclusive 550 final NetworkMonitor monitor = runNetworkTest(VALIDATION_RESULT_INVALID); 551 assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); 552 verify(mFallbackConnection, times(1)).getResponseCode(); 553 verify(mOtherFallbackConnection, never()).getResponseCode(); 554 555 // Second check uses the URL chosen by Random 556 final CaptivePortalProbeResult result = monitor.isCaptivePortal(); 557 assertTrue(result.isPortal()); 558 verify(mOtherFallbackConnection, times(1)).getResponseCode(); 559 } 560 561 @Test testIsCaptivePortal_AllProbesFailed()562 public void testIsCaptivePortal_AllProbesFailed() throws IOException { 563 setSslException(mHttpsConnection); 564 setStatus(mHttpConnection, 500); 565 setStatus(mFallbackConnection, 404); 566 567 runFailedNetworkTest(); 568 verify(mFallbackConnection, times(1)).getResponseCode(); 569 verify(mOtherFallbackConnection, never()).getResponseCode(); 570 } 571 572 @Test testIsCaptivePortal_InvalidUrlSkipped()573 public void testIsCaptivePortal_InvalidUrlSkipped() throws IOException { 574 setFallbackUrl("invalid"); 575 setOtherFallbackUrls("otherinvalid," + TEST_OTHER_FALLBACK_URL + ",yetanotherinvalid"); 576 577 setSslException(mHttpsConnection); 578 setStatus(mHttpConnection, 500); 579 setPortal302(mOtherFallbackConnection); 580 runPortalNetworkTest(VALIDATION_RESULT_INVALID); 581 verify(mOtherFallbackConnection, times(1)).getResponseCode(); 582 verify(mFallbackConnection, never()).getResponseCode(); 583 } 584 setupFallbackSpec()585 private void setupFallbackSpec() throws IOException { 586 setFallbackSpecs("http://example.com@@/@@204@@/@@" 587 + "@@,@@" 588 + TEST_OTHER_FALLBACK_URL + "@@/@@30[12]@@/@@https://(www\\.)?google.com/?.*"); 589 590 setSslException(mHttpsConnection); 591 setStatus(mHttpConnection, 500); 592 593 // Use the 2nd fallback spec 594 when(mRandom.nextInt()).thenReturn(1); 595 } 596 597 @Test testIsCaptivePortal_FallbackSpecIsPartial()598 public void testIsCaptivePortal_FallbackSpecIsPartial() throws IOException { 599 setupFallbackSpec(); 600 set302(mOtherFallbackConnection, "https://www.google.com/test?q=3"); 601 602 // HTTPS failed, fallback spec went through -> partial connectivity 603 runPartialConnectivityNetworkTest(VALIDATION_RESULT_FALLBACK_PARTIAL); 604 verify(mOtherFallbackConnection, times(1)).getResponseCode(); 605 verify(mFallbackConnection, never()).getResponseCode(); 606 } 607 608 @Test testIsCaptivePortal_FallbackSpecIsPortal()609 public void testIsCaptivePortal_FallbackSpecIsPortal() throws IOException { 610 setupFallbackSpec(); 611 set302(mOtherFallbackConnection, "http://login.portal.example.com"); 612 runPortalNetworkTest(VALIDATION_RESULT_INVALID); 613 } 614 615 @Test testIsCaptivePortal_IgnorePortals()616 public void testIsCaptivePortal_IgnorePortals() throws IOException { 617 setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE); 618 setSslException(mHttpsConnection); 619 setPortal302(mHttpConnection); 620 621 runNoValidationNetworkTest(); 622 } 623 624 @Test testIsDataStall_EvaluationDisabled()625 public void testIsDataStall_EvaluationDisabled() { 626 setDataStallEvaluationType(0); 627 WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor(); 628 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); 629 assertFalse(wrappedMonitor.isDataStall()); 630 } 631 632 @Test testIsDataStall_EvaluationDnsOnNotMeteredNetwork()633 public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() { 634 WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor(); 635 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); 636 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); 637 assertTrue(wrappedMonitor.isDataStall()); 638 } 639 640 @Test testIsDataStall_EvaluationDnsOnMeteredNetwork()641 public void testIsDataStall_EvaluationDnsOnMeteredNetwork() { 642 WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor(); 643 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); 644 assertFalse(wrappedMonitor.isDataStall()); 645 646 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); 647 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); 648 assertTrue(wrappedMonitor.isDataStall()); 649 } 650 651 @Test testIsDataStall_EvaluationDnsWithDnsTimeoutCount()652 public void testIsDataStall_EvaluationDnsWithDnsTimeoutCount() { 653 WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor(); 654 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); 655 makeDnsTimeoutEvent(wrappedMonitor, 3); 656 assertFalse(wrappedMonitor.isDataStall()); 657 // Reset consecutive timeout counts. 658 makeDnsSuccessEvent(wrappedMonitor, 1); 659 makeDnsTimeoutEvent(wrappedMonitor, 2); 660 assertFalse(wrappedMonitor.isDataStall()); 661 662 makeDnsTimeoutEvent(wrappedMonitor, 3); 663 assertTrue(wrappedMonitor.isDataStall()); 664 665 // Set the value to larger than the default dns log size. 666 setConsecutiveDnsTimeoutThreshold(51); 667 wrappedMonitor = makeMeteredNetworkMonitor(); 668 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); 669 makeDnsTimeoutEvent(wrappedMonitor, 50); 670 assertFalse(wrappedMonitor.isDataStall()); 671 672 makeDnsTimeoutEvent(wrappedMonitor, 1); 673 assertTrue(wrappedMonitor.isDataStall()); 674 } 675 676 @Test testIsDataStall_EvaluationDnsWithDnsTimeThreshold()677 public void testIsDataStall_EvaluationDnsWithDnsTimeThreshold() { 678 // Test dns events happened in valid dns time threshold. 679 WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor(); 680 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); 681 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); 682 assertFalse(wrappedMonitor.isDataStall()); 683 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); 684 assertTrue(wrappedMonitor.isDataStall()); 685 686 // Test dns events happened before valid dns time threshold. 687 setValidDataStallDnsTimeThreshold(0); 688 wrappedMonitor = makeMeteredNetworkMonitor(); 689 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); 690 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); 691 assertFalse(wrappedMonitor.isDataStall()); 692 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); 693 assertFalse(wrappedMonitor.isDataStall()); 694 } 695 696 @Test testBrokenNetworkNotValidated()697 public void testBrokenNetworkNotValidated() throws Exception { 698 setSslException(mHttpsConnection); 699 setStatus(mHttpConnection, 500); 700 setStatus(mFallbackConnection, 404); 701 702 runFailedNetworkTest(); 703 } 704 705 @Test testNoInternetCapabilityValidated()706 public void testNoInternetCapabilityValidated() throws Exception { 707 runNetworkTest(NO_INTERNET_CAPABILITIES, NETWORK_VALIDATION_RESULT_VALID); 708 verify(mCleartextDnsNetwork, never()).openConnection(any()); 709 } 710 711 @Test testLaunchCaptivePortalApp()712 public void testLaunchCaptivePortalApp() throws Exception { 713 setSslException(mHttpsConnection); 714 setPortal302(mHttpConnection); 715 716 final NetworkMonitor nm = makeMonitor(METERED_CAPABILITIES); 717 nm.notifyNetworkConnected(TEST_LINK_PROPERTIES, METERED_CAPABILITIES); 718 719 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) 720 .showProvisioningNotification(any(), any()); 721 722 assertEquals(1, mRegisteredReceivers.size()); 723 724 // Check that startCaptivePortalApp sends the expected intent. 725 nm.launchCaptivePortalApp(); 726 727 final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); 728 final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class); 729 verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1)) 730 .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture()); 731 final Bundle bundle = bundleCaptor.getValue(); 732 final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK); 733 assertEquals(TEST_NETID, bundleNetwork.netId); 734 // network is passed both in bundle and as parameter, as the bundle is opaque to the 735 // framework and only intended for the captive portal app, but the framework needs 736 // the network to identify the right NetworkMonitor. 737 assertEquals(TEST_NETID, networkCaptor.getValue().netId); 738 739 // Have the app report that the captive portal is dismissed, and check that we revalidate. 740 setStatus(mHttpsConnection, 204); 741 setStatus(mHttpConnection, 204); 742 743 resetCallbacks(); 744 nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED); 745 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()) 746 .notifyNetworkTested(eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP 747 | NETWORK_VALIDATION_RESULT_VALID), any()); 748 assertEquals(0, mRegisteredReceivers.size()); 749 } 750 751 @Test testPrivateDnsSuccess()752 public void testPrivateDnsSuccess() throws Exception { 753 setStatus(mHttpsConnection, 204); 754 setStatus(mHttpConnection, 204); 755 mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::53"}); 756 757 WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); 758 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0])); 759 wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, NOT_METERED_CAPABILITIES); 760 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) 761 .notifyNetworkTested(eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS), 762 eq(null)); 763 } 764 765 @Test testPrivateDnsResolutionRetryUpdate()766 public void testPrivateDnsResolutionRetryUpdate() throws Exception { 767 // Set a private DNS hostname that doesn't resolve and expect validation to fail. 768 mFakeDns.setAnswer("dns.google", new String[0]); 769 setStatus(mHttpsConnection, 204); 770 setStatus(mHttpConnection, 204); 771 772 WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); 773 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0])); 774 wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, NOT_METERED_CAPABILITIES); 775 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()) 776 .notifyNetworkTested( 777 eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS), 778 eq(null)); 779 780 // Fix DNS and retry, expect validation to succeed. 781 resetCallbacks(); 782 mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::1"}); 783 784 wnm.forceReevaluation(Process.myUid()); 785 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()) 786 .notifyNetworkTested(eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS), 787 eq(null)); 788 789 // Change configuration to an invalid DNS name, expect validation to fail. 790 resetCallbacks(); 791 mFakeDns.setAnswer("dns.bad", new String[0]); 792 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.bad", new InetAddress[0])); 793 // Strict mode hostname resolve fail. Expect only notification for evaluation fail. No probe 794 // notification. 795 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)) 796 .notifyNetworkTested(eq(NETWORK_VALIDATION_PROBE_DNS 797 | NETWORK_VALIDATION_PROBE_HTTPS), eq(null)); 798 799 // Change configuration back to working again, but make private DNS not work. 800 // Expect validation to fail. 801 resetCallbacks(); 802 mFakeDns.setNonBypassPrivateDnsWorking(false); 803 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", 804 new InetAddress[0])); 805 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()) 806 .notifyNetworkTested( 807 eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS), 808 eq(null)); 809 810 // Make private DNS work again. Expect validation to succeed. 811 resetCallbacks(); 812 mFakeDns.setNonBypassPrivateDnsWorking(true); 813 wnm.forceReevaluation(Process.myUid()); 814 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()) 815 .notifyNetworkTested( 816 eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS), eq(null)); 817 } 818 819 @Test testDataStall_StallSuspectedAndSendMetrics()820 public void testDataStall_StallSuspectedAndSendMetrics() throws IOException { 821 WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor(); 822 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); 823 makeDnsTimeoutEvent(wrappedMonitor, 5); 824 assertTrue(wrappedMonitor.isDataStall()); 825 verify(mDataStallStatsUtils, times(1)).write(makeEmptyDataStallDetectionStats(), any()); 826 } 827 828 @Test testDataStall_NoStallSuspectedAndSendMetrics()829 public void testDataStall_NoStallSuspectedAndSendMetrics() throws IOException { 830 WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor(); 831 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); 832 makeDnsTimeoutEvent(wrappedMonitor, 3); 833 assertFalse(wrappedMonitor.isDataStall()); 834 verify(mDataStallStatsUtils, never()).write(makeEmptyDataStallDetectionStats(), any()); 835 } 836 837 @Test testCollectDataStallMetrics()838 public void testCollectDataStallMetrics() { 839 WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor(); 840 841 when(mTelephony.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE); 842 when(mTelephony.getNetworkOperator()).thenReturn(TEST_MCCMNC); 843 when(mTelephony.getSimOperator()).thenReturn(TEST_MCCMNC); 844 845 DataStallDetectionStats.Builder stats = 846 new DataStallDetectionStats.Builder() 847 .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS) 848 .setNetworkType(NetworkCapabilities.TRANSPORT_CELLULAR) 849 .setCellData(TelephonyManager.NETWORK_TYPE_LTE /* radioType */, 850 true /* roaming */, 851 TEST_MCCMNC /* networkMccmnc */, 852 TEST_MCCMNC /* simMccmnc */, 853 CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN /* signalStrength */); 854 generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD); 855 856 assertEquals(wrappedMonitor.buildDataStallDetectionStats( 857 NetworkCapabilities.TRANSPORT_CELLULAR), stats.build()); 858 859 when(mWifi.getConnectionInfo()).thenReturn(mWifiInfo); 860 861 stats = new DataStallDetectionStats.Builder() 862 .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS) 863 .setNetworkType(NetworkCapabilities.TRANSPORT_WIFI) 864 .setWiFiData(mWifiInfo); 865 generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD); 866 867 assertEquals( 868 wrappedMonitor.buildDataStallDetectionStats(NetworkCapabilities.TRANSPORT_WIFI), 869 stats.build()); 870 } 871 872 @Test testIgnoreHttpsProbe()873 public void testIgnoreHttpsProbe() throws Exception { 874 setSslException(mHttpsConnection); 875 setStatus(mHttpConnection, 204); 876 // Expect to send HTTP, HTTPS, FALLBACK probe and evaluation result notifications to CS. 877 final NetworkMonitor nm = runNetworkTest(VALIDATION_RESULT_PARTIAL); 878 879 resetCallbacks(); 880 nm.setAcceptPartialConnectivity(); 881 // Expect to update evaluation result notifications to CS. 882 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested( 883 eq(VALIDATION_RESULT_PARTIAL | NETWORK_VALIDATION_RESULT_VALID), eq(null)); 884 } 885 886 @Test testIsPartialConnectivity()887 public void testIsPartialConnectivity() throws IOException { 888 setStatus(mHttpsConnection, 500); 889 setStatus(mHttpConnection, 204); 890 setStatus(mFallbackConnection, 500); 891 runPartialConnectivityNetworkTest(VALIDATION_RESULT_PARTIAL); 892 893 resetCallbacks(); 894 setStatus(mHttpsConnection, 500); 895 setStatus(mHttpConnection, 500); 896 setStatus(mFallbackConnection, 204); 897 runPartialConnectivityNetworkTest(VALIDATION_RESULT_FALLBACK_PARTIAL); 898 } 899 assertIpAddressArrayEquals(String[] expected, InetAddress[] actual)900 private void assertIpAddressArrayEquals(String[] expected, InetAddress[] actual) { 901 String[] actualStrings = new String[actual.length]; 902 for (int i = 0; i < actual.length; i++) { 903 actualStrings[i] = actual[i].getHostAddress(); 904 } 905 assertArrayEquals("Array of IP addresses differs", expected, actualStrings); 906 } 907 908 @Test testSendDnsProbeWithTimeout()909 public void testSendDnsProbeWithTimeout() throws Exception { 910 WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); 911 final int shortTimeoutMs = 200; 912 913 // Clear the wildcard DNS response created in setUp. 914 mFakeDns.setAnswer("*", null); 915 916 String[] expected = new String[]{"2001:db8::"}; 917 mFakeDns.setAnswer("www.google.com", expected); 918 InetAddress[] actual = wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); 919 assertIpAddressArrayEquals(expected, actual); 920 921 expected = new String[]{"2001:db8::", "192.0.2.1"}; 922 mFakeDns.setAnswer("www.googleapis.com", expected); 923 actual = wnm.sendDnsProbeWithTimeout("www.googleapis.com", shortTimeoutMs); 924 assertIpAddressArrayEquals(expected, actual); 925 926 mFakeDns.setAnswer("www.google.com", new String[0]); 927 try { 928 wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); 929 fail("No DNS results, expected UnknownHostException"); 930 } catch (UnknownHostException e) { 931 } 932 933 mFakeDns.setAnswer("www.google.com", null); 934 try { 935 wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); 936 fail("DNS query timed out, expected UnknownHostException"); 937 } catch (UnknownHostException e) { 938 } 939 } 940 941 @Test testNotifyNetwork_WithforceReevaluation()942 public void testNotifyNetwork_WithforceReevaluation() throws Exception { 943 final NetworkMonitor nm = runValidatedNetworkTest(); 944 // Verify forceReevalution will not reset the validation result but only probe result until 945 // getting the validation result. 946 resetCallbacks(); 947 setSslException(mHttpsConnection); 948 setStatus(mHttpConnection, 500); 949 setStatus(mFallbackConnection, 204); 950 nm.forceReevaluation(Process.myUid()); 951 final ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class); 952 // Expect to send HTTP, HTTPs, FALLBACK and evaluation results. 953 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)) 954 .notifyNetworkTested(eq(NETWORK_VALIDATION_PROBE_DNS | 955 NETWORK_VALIDATION_PROBE_FALLBACK | NETWORK_VALIDATION_RESULT_PARTIAL), any()); 956 } 957 958 @Test testEvaluationState_clearProbeResults()959 public void testEvaluationState_clearProbeResults() throws Exception { 960 final NetworkMonitor nm = runValidatedNetworkTest(); 961 nm.getEvaluationState().clearProbeResults(); 962 // Verify probe results are all reset and only evaluation result left. 963 assertEquals(NETWORK_VALIDATION_RESULT_VALID, 964 nm.getEvaluationState().getNetworkTestResult()); 965 } 966 967 @Test testEvaluationState_reportProbeResult()968 public void testEvaluationState_reportProbeResult() throws Exception { 969 final NetworkMonitor nm = runValidatedNetworkTest(); 970 971 resetCallbacks(); 972 973 nm.reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, CaptivePortalProbeResult.SUCCESS); 974 // Verify result should be appended and notifyNetworkTested callback is triggered once. 975 assertEquals(nm.getEvaluationState().getNetworkTestResult(), 976 VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_HTTP); 977 978 nm.reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, CaptivePortalProbeResult.FAILED); 979 // Verify DNS probe result should not be cleared. 980 assertTrue((nm.getEvaluationState().getNetworkTestResult() & NETWORK_VALIDATION_PROBE_DNS) 981 == NETWORK_VALIDATION_PROBE_DNS); 982 } 983 984 @Test testEvaluationState_reportEvaluationResult()985 public void testEvaluationState_reportEvaluationResult() throws Exception { 986 final NetworkMonitor nm = runValidatedNetworkTest(); 987 988 nm.getEvaluationState().reportEvaluationResult(NETWORK_VALIDATION_RESULT_PARTIAL, 989 null /* redirectUrl */); 990 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested( 991 eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS 992 | NETWORK_VALIDATION_RESULT_PARTIAL), eq(null)); 993 994 nm.getEvaluationState().reportEvaluationResult( 995 NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_PARTIAL, 996 null /* redirectUrl */); 997 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested( 998 eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_PARTIAL), eq(null)); 999 1000 nm.getEvaluationState().reportEvaluationResult(VALIDATION_RESULT_INVALID, 1001 TEST_REDIRECT_URL); 1002 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested( 1003 eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS), 1004 eq(TEST_REDIRECT_URL)); 1005 } 1006 makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count)1007 private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) { 1008 for (int i = 0; i < count; i++) { 1009 wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount( 1010 RETURN_CODE_DNS_TIMEOUT); 1011 } 1012 } 1013 makeDnsSuccessEvent(WrappedNetworkMonitor wrappedMonitor, int count)1014 private void makeDnsSuccessEvent(WrappedNetworkMonitor wrappedMonitor, int count) { 1015 for (int i = 0; i < count; i++) { 1016 wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount( 1017 RETURN_CODE_DNS_SUCCESS); 1018 } 1019 } 1020 makeEmptyDataStallDetectionStats()1021 private DataStallDetectionStats makeEmptyDataStallDetectionStats() { 1022 return new DataStallDetectionStats.Builder().build(); 1023 } 1024 setDataStallEvaluationType(int type)1025 private void setDataStallEvaluationType(int type) { 1026 when(mDependencies.getDeviceConfigPropertyInt(any(), 1027 eq(CONFIG_DATA_STALL_EVALUATION_TYPE), anyInt())).thenReturn(type); 1028 } 1029 setMinDataStallEvaluateInterval(int time)1030 private void setMinDataStallEvaluateInterval(int time) { 1031 when(mDependencies.getDeviceConfigPropertyInt(any(), 1032 eq(CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL), anyInt())).thenReturn(time); 1033 } 1034 setValidDataStallDnsTimeThreshold(int time)1035 private void setValidDataStallDnsTimeThreshold(int time) { 1036 when(mDependencies.getDeviceConfigPropertyInt(any(), 1037 eq(CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD), anyInt())).thenReturn(time); 1038 } 1039 setConsecutiveDnsTimeoutThreshold(int num)1040 private void setConsecutiveDnsTimeoutThreshold(int num) { 1041 when(mDependencies.getDeviceConfigPropertyInt(any(), 1042 eq(CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD), anyInt())).thenReturn(num); 1043 } 1044 setFallbackUrl(String url)1045 private void setFallbackUrl(String url) { 1046 when(mDependencies.getSetting(any(), 1047 eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL), any())).thenReturn(url); 1048 } 1049 setOtherFallbackUrls(String urls)1050 private void setOtherFallbackUrls(String urls) { 1051 when(mDependencies.getDeviceConfigProperty(any(), 1052 eq(CAPTIVE_PORTAL_OTHER_FALLBACK_URLS), any())).thenReturn(urls); 1053 } 1054 setFallbackSpecs(String specs)1055 private void setFallbackSpecs(String specs) { 1056 when(mDependencies.getDeviceConfigProperty(any(), 1057 eq(CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs); 1058 } 1059 setCaptivePortalMode(int mode)1060 private void setCaptivePortalMode(int mode) { 1061 when(mDependencies.getSetting(any(), 1062 eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())).thenReturn(mode); 1063 } 1064 runPortalNetworkTest(int result)1065 private void runPortalNetworkTest(int result) { 1066 runNetworkTest(result); 1067 assertEquals(1, mRegisteredReceivers.size()); 1068 assertNotNull(mNetworkTestedRedirectUrlCaptor.getValue()); 1069 } 1070 runNotPortalNetworkTest()1071 private void runNotPortalNetworkTest() { 1072 runNetworkTest(VALIDATION_RESULT_VALID); 1073 assertEquals(0, mRegisteredReceivers.size()); 1074 assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); 1075 } 1076 runNoValidationNetworkTest()1077 private void runNoValidationNetworkTest() { 1078 runNetworkTest(NETWORK_VALIDATION_RESULT_VALID); 1079 assertEquals(0, mRegisteredReceivers.size()); 1080 assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); 1081 } 1082 runFailedNetworkTest()1083 private void runFailedNetworkTest() { 1084 runNetworkTest(VALIDATION_RESULT_INVALID); 1085 assertEquals(0, mRegisteredReceivers.size()); 1086 assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); 1087 } 1088 runPartialConnectivityNetworkTest(int result)1089 private void runPartialConnectivityNetworkTest(int result) { 1090 runNetworkTest(result); 1091 assertEquals(0, mRegisteredReceivers.size()); 1092 assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); 1093 } 1094 runValidatedNetworkTest()1095 private NetworkMonitor runValidatedNetworkTest() throws Exception { 1096 setStatus(mHttpsConnection, 204); 1097 setStatus(mHttpConnection, 204); 1098 // Expect to send HTTPs and evaluation results. 1099 return runNetworkTest(VALIDATION_RESULT_VALID); 1100 } 1101 runNetworkTest(int testResult)1102 private NetworkMonitor runNetworkTest(int testResult) { 1103 return runNetworkTest(METERED_CAPABILITIES, testResult); 1104 } 1105 runNetworkTest(NetworkCapabilities nc, int testResult)1106 private NetworkMonitor runNetworkTest(NetworkCapabilities nc, int testResult) { 1107 final NetworkMonitor monitor = makeMonitor(nc); 1108 monitor.notifyNetworkConnected(TEST_LINK_PROPERTIES, nc); 1109 try { 1110 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)) 1111 .notifyNetworkTested(eq(testResult), mNetworkTestedRedirectUrlCaptor.capture()); 1112 } catch (RemoteException e) { 1113 fail("Unexpected exception: " + e); 1114 } 1115 waitForIdle(monitor.getHandler()); 1116 1117 return monitor; 1118 } 1119 setSslException(HttpURLConnection connection)1120 private void setSslException(HttpURLConnection connection) throws IOException { 1121 doThrow(new SSLHandshakeException("Invalid cert")).when(connection).getResponseCode(); 1122 } 1123 set302(HttpURLConnection connection, String location)1124 private void set302(HttpURLConnection connection, String location) throws IOException { 1125 setStatus(connection, 302); 1126 doReturn(location).when(connection).getHeaderField(LOCATION_HEADER); 1127 } 1128 setPortal302(HttpURLConnection connection)1129 private void setPortal302(HttpURLConnection connection) throws IOException { 1130 set302(connection, "http://login.example.com"); 1131 } 1132 setStatus(HttpURLConnection connection, int status)1133 private void setStatus(HttpURLConnection connection, int status) throws IOException { 1134 doReturn(status).when(connection).getResponseCode(); 1135 } 1136 generateTimeoutDnsEvent(DataStallDetectionStats.Builder stats, int num)1137 private void generateTimeoutDnsEvent(DataStallDetectionStats.Builder stats, int num) { 1138 for (int i = 0; i < num; i++) { 1139 stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, 123456789 /* timeMs */); 1140 } 1141 } 1142 1143 } 1144 1145