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