blob: 8f0974dc85d2eed5e4846d8269d4b1509960d60f [file] [log] [blame]
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001/*
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
17package com.android.server.connectivity;
18
Remi NGUYEN VANad99e542019-02-13 20:58:59 +090019import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
Chiachang Wang813ee472019-05-23 16:29:30 +080020import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
21import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK;
22import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP;
23import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS;
24import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
25import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
26import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090027import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
Chiachang Wang9a87f802019-04-08 19:06:21 +080028import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD;
29import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_EVALUATION_TYPE;
30import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL;
31import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD;
32import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS;
Chiachang Wang79a6da32019-04-17 17:00:54 +080033import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS;
34import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS;
35import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090036
37import static junit.framework.Assert.assertEquals;
38import static junit.framework.Assert.assertFalse;
39
Lorenzo Colitti171cfd22019-04-18 13:44:32 +090040import static org.junit.Assert.assertArrayEquals;
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +090041import static org.junit.Assert.assertNotNull;
42import static org.junit.Assert.assertNull;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090043import static org.junit.Assert.assertTrue;
44import static org.junit.Assert.fail;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090045import static org.mockito.ArgumentMatchers.eq;
46import static org.mockito.Mockito.any;
47import static org.mockito.Mockito.anyInt;
48import static org.mockito.Mockito.doAnswer;
49import static org.mockito.Mockito.doReturn;
50import static org.mockito.Mockito.doThrow;
51import static org.mockito.Mockito.never;
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +090052import static org.mockito.Mockito.reset;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090053import static org.mockito.Mockito.timeout;
54import static org.mockito.Mockito.times;
55import static org.mockito.Mockito.verify;
56import static org.mockito.Mockito.when;
57
Chiachang Wangcaa35202019-02-26 11:32:18 +080058import android.annotation.NonNull;
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +090059import android.content.BroadcastReceiver;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090060import android.content.Context;
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +090061import android.content.Intent;
Niklas Lindgren0c904882018-12-07 11:08:04 +010062import android.content.res.Resources;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090063import android.net.ConnectivityManager;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +090064import android.net.DnsResolver;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090065import android.net.INetworkMonitorCallbacks;
66import android.net.InetAddresses;
67import android.net.LinkProperties;
68import android.net.Network;
69import android.net.NetworkCapabilities;
70import android.net.NetworkInfo;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090071import android.net.captiveportal.CaptivePortalProbeResult;
72import android.net.metrics.IpConnectivityLog;
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +090073import android.net.shared.PrivateDnsConfig;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090074import android.net.util.SharedLog;
Chiachang Wangcaa35202019-02-26 11:32:18 +080075import android.net.wifi.WifiInfo;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090076import android.net.wifi.WifiManager;
Remi NGUYEN VANad99e542019-02-13 20:58:59 +090077import android.os.Bundle;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090078import android.os.ConditionVariable;
79import android.os.Handler;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +090080import android.os.Looper;
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +090081import android.os.Process;
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +090082import android.os.RemoteException;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090083import android.os.SystemClock;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090084import android.provider.Settings;
Chiachang Wangcaa35202019-02-26 11:32:18 +080085import android.telephony.CellSignalStrength;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090086import android.telephony.TelephonyManager;
87import android.util.ArrayMap;
88
Brett Chabot189c5982019-02-26 14:52:33 -080089import androidx.test.filters.SmallTest;
90import androidx.test.runner.AndroidJUnit4;
91
Lorenzo Colitti171cfd22019-04-18 13:44:32 +090092import com.android.networkstack.R;
Chiachang Wang80242272019-04-11 21:24:28 +080093import com.android.networkstack.metrics.DataStallDetectionStats;
94import com.android.networkstack.metrics.DataStallStatsUtils;
Chalard Jean0d0cc8c2019-05-28 16:48:32 +090095import com.android.testutils.HandlerUtilsKt;
Chiachang Wang80242272019-04-11 21:24:28 +080096
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +090097import org.junit.After;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090098import org.junit.Before;
99import org.junit.Test;
100import org.junit.runner.RunWith;
101import org.mockito.ArgumentCaptor;
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900102import org.mockito.Captor;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900103import org.mockito.Mock;
104import org.mockito.MockitoAnnotations;
105import org.mockito.Spy;
Chiachang Wangddb7da62019-06-03 15:50:53 +0800106import org.mockito.invocation.InvocationOnMock;
107import org.mockito.stubbing.Answer;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900108
109import java.io.IOException;
110import java.net.HttpURLConnection;
111import java.net.InetAddress;
112import java.net.URL;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900113import java.net.UnknownHostException;
114import java.util.ArrayList;
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900115import java.util.HashSet;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900116import java.util.List;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900117import java.util.Random;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900118import java.util.concurrent.Executor;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900119
120import javax.net.ssl.SSLHandshakeException;
121
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900122@RunWith(AndroidJUnit4.class)
123@SmallTest
124public class NetworkMonitorTest {
125 private static final String LOCATION_HEADER = "location";
126
127 private @Mock Context mContext;
Niklas Lindgren0c904882018-12-07 11:08:04 +0100128 private @Mock Resources mResources;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900129 private @Mock IpConnectivityLog mLogger;
130 private @Mock SharedLog mValidationLogger;
131 private @Mock NetworkInfo mNetworkInfo;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900132 private @Mock DnsResolver mDnsResolver;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900133 private @Mock ConnectivityManager mCm;
134 private @Mock TelephonyManager mTelephony;
135 private @Mock WifiManager mWifi;
136 private @Mock HttpURLConnection mHttpConnection;
137 private @Mock HttpURLConnection mHttpsConnection;
138 private @Mock HttpURLConnection mFallbackConnection;
139 private @Mock HttpURLConnection mOtherFallbackConnection;
140 private @Mock Random mRandom;
141 private @Mock NetworkMonitor.Dependencies mDependencies;
142 private @Mock INetworkMonitorCallbacks mCallbacks;
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900143 private @Spy Network mCleartextDnsNetwork = new Network(TEST_NETID);
144 private @Mock Network mNetwork;
Chiachang Wang8b5f84a2019-02-22 11:13:07 +0800145 private @Mock DataStallStatsUtils mDataStallStatsUtils;
Chiachang Wangcaa35202019-02-26 11:32:18 +0800146 private @Mock WifiInfo mWifiInfo;
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900147 private @Captor ArgumentCaptor<String> mNetworkTestedRedirectUrlCaptor;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900148
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900149 private HashSet<WrappedNetworkMonitor> mCreatedNetworkMonitors;
150 private HashSet<BroadcastReceiver> mRegisteredReceivers;
151
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900152 private static final int TEST_NETID = 4242;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900153 private static final String TEST_HTTP_URL = "http://www.google.com/gen_204";
154 private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204";
155 private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204";
156 private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204";
Chiachang Wangcaa35202019-02-26 11:32:18 +0800157 private static final String TEST_MCCMNC = "123456";
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900158
Chiachang Wang813ee472019-05-23 16:29:30 +0800159 private static final int VALIDATION_RESULT_INVALID = 0;
160 private static final int VALIDATION_RESULT_PORTAL = 0;
161 private static final String TEST_REDIRECT_URL = "android.com";
162 private static final int VALIDATION_RESULT_PARTIAL = NETWORK_VALIDATION_PROBE_DNS
163 | NETWORK_VALIDATION_PROBE_HTTP
164 | NETWORK_VALIDATION_RESULT_PARTIAL;
165 private static final int VALIDATION_RESULT_FALLBACK_PARTIAL = NETWORK_VALIDATION_PROBE_DNS
166 | NETWORK_VALIDATION_PROBE_FALLBACK
167 | NETWORK_VALIDATION_RESULT_PARTIAL;
168 private static final int VALIDATION_RESULT_VALID = NETWORK_VALIDATION_PROBE_DNS
169 | NETWORK_VALIDATION_PROBE_HTTPS
170 | NETWORK_VALIDATION_RESULT_VALID;
171
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900172 private static final int RETURN_CODE_DNS_SUCCESS = 0;
173 private static final int RETURN_CODE_DNS_TIMEOUT = 255;
Chiachang Wangcaa35202019-02-26 11:32:18 +0800174 private static final int DEFAULT_DNS_TIMEOUT_THRESHOLD = 5;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900175
176 private static final int HANDLER_TIMEOUT_MS = 1000;
177
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900178 private static final LinkProperties TEST_LINK_PROPERTIES = new LinkProperties();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900179
180 private static final NetworkCapabilities METERED_CAPABILITIES = new NetworkCapabilities()
181 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
182 .addCapability(NET_CAPABILITY_INTERNET);
183
184 private static final NetworkCapabilities NOT_METERED_CAPABILITIES = new NetworkCapabilities()
185 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
186 .addCapability(NET_CAPABILITY_INTERNET)
187 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
188
189 private static final NetworkCapabilities NO_INTERNET_CAPABILITIES = new NetworkCapabilities()
190 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
191
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900192 /**
193 * Fakes DNS responses.
194 *
195 * Allows test methods to configure the IP addresses that will be resolved by
196 * Network#getAllByName and by DnsResolver#query.
197 */
198 class FakeDns {
199 private final ArrayMap<String, List<InetAddress>> mAnswers = new ArrayMap<>();
200 private boolean mNonBypassPrivateDnsWorking = true;
201
202 /** Whether DNS queries on mNonBypassPrivateDnsWorking should succeed. */
203 private void setNonBypassPrivateDnsWorking(boolean working) {
204 mNonBypassPrivateDnsWorking = working;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900205 }
206
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900207 /** Clears all DNS entries. */
208 private synchronized void clearAll() {
209 mAnswers.clear();
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900210 }
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900211
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900212 /** Returns the answer for a given name on the given mock network. */
213 private synchronized List<InetAddress> getAnswer(Object mock, String hostname) {
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900214 if (mock == mNetwork && !mNonBypassPrivateDnsWorking) {
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900215 return null;
216 }
217 if (mAnswers.containsKey(hostname)) {
218 return mAnswers.get(hostname);
219 }
220 return mAnswers.get("*");
221 }
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900222
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900223 /** Sets the answer for a given name. */
224 private synchronized void setAnswer(String hostname, String[] answer)
225 throws UnknownHostException {
226 if (answer == null) {
227 mAnswers.remove(hostname);
228 } else {
229 List<InetAddress> answerList = new ArrayList<>();
230 for (String addr : answer) {
231 answerList.add(InetAddresses.parseNumericAddress(addr));
232 }
233 mAnswers.put(hostname, answerList);
234 }
235 }
236
237 /** Simulates a getAllByName call for the specified name on the specified mock network. */
238 private InetAddress[] getAllByName(Object mock, String hostname)
239 throws UnknownHostException {
240 List<InetAddress> answer = getAnswer(mock, hostname);
241 if (answer == null || answer.size() == 0) {
242 throw new UnknownHostException(hostname);
243 }
244 return answer.toArray(new InetAddress[0]);
245 }
246
247 /** Starts mocking DNS queries. */
248 private void startMocking() throws UnknownHostException {
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900249 // Queries on mNetwork using getAllByName.
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900250 doAnswer(invocation -> {
251 return getAllByName(invocation.getMock(), invocation.getArgument(0));
252 }).when(mNetwork).getAllByName(any());
253
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900254 // Queries on mCleartextDnsNetwork using DnsResolver#query.
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900255 doAnswer(invocation -> {
Chiachang Wangddb7da62019-06-03 15:50:53 +0800256 return mockQuery(invocation, 1 /* posHostname */, 3 /* posExecutor */,
257 5 /* posCallback */);
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900258 }).when(mDnsResolver).query(any(), any(), anyInt(), any(), any(), any());
Chiachang Wang1c67f4e2019-05-09 21:28:47 +0800259
Chiachang Wangddb7da62019-06-03 15:50:53 +0800260 // Queries on mCleartextDnsNetwork using DnsResolver#query with QueryType.
Chiachang Wang1c67f4e2019-05-09 21:28:47 +0800261 doAnswer(invocation -> {
Chiachang Wangddb7da62019-06-03 15:50:53 +0800262 return mockQuery(invocation, 1 /* posHostname */, 4 /* posExecutor */,
263 6 /* posCallback */);
Chiachang Wang1c67f4e2019-05-09 21:28:47 +0800264 }).when(mDnsResolver).query(any(), any(), anyInt(), anyInt(), any(), any(), any());
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900265 }
Chiachang Wangddb7da62019-06-03 15:50:53 +0800266
267 // Mocking queries on DnsResolver#query.
268 private Answer mockQuery(InvocationOnMock invocation, int posHostname, int posExecutor,
269 int posCallback) {
270 String hostname = (String) invocation.getArgument(posHostname);
271 Executor executor = (Executor) invocation.getArgument(posExecutor);
272 DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(posCallback);
273
274 List<InetAddress> answer = getAnswer(invocation.getMock(), hostname);
275 if (answer != null && answer.size() > 0) {
276 new Handler(Looper.getMainLooper()).post(() -> {
277 executor.execute(() -> callback.onAnswer(answer, 0));
278 });
279 }
280 // If no answers, do nothing. sendDnsProbeWithTimeout will time out and throw UHE.
281 return null;
282 }
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900283 }
284
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900285 private FakeDns mFakeDns;
286
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900287 @Before
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900288 public void setUp() throws Exception {
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900289 MockitoAnnotations.initMocks(this);
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900290 when(mDependencies.getPrivateDnsBypassNetwork(any())).thenReturn(mCleartextDnsNetwork);
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900291 when(mDependencies.getDnsResolver()).thenReturn(mDnsResolver);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900292 when(mDependencies.getRandom()).thenReturn(mRandom);
293 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt()))
294 .thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
Chiachang Wang79a6da32019-04-17 17:00:54 +0800295 when(mDependencies.getDeviceConfigPropertyInt(any(), eq(CAPTIVE_PORTAL_USE_HTTPS),
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900296 anyInt())).thenReturn(1);
Niklas Lindgren0c904882018-12-07 11:08:04 +0100297 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any()))
298 .thenReturn(TEST_HTTP_URL);
299 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL), any()))
300 .thenReturn(TEST_HTTPS_URL);
301
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900302 doReturn(mCleartextDnsNetwork).when(mNetwork).getPrivateDnsBypassingCopy();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900303
304 when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mCm);
305 when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephony);
306 when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifi);
Niklas Lindgren0c904882018-12-07 11:08:04 +0100307 when(mContext.getResources()).thenReturn(mResources);
308
309 when(mResources.getString(anyInt())).thenReturn("");
310 when(mResources.getStringArray(anyInt())).thenReturn(new String[0]);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900311
312 when(mNetworkInfo.getType()).thenReturn(ConnectivityManager.TYPE_WIFI);
313 setFallbackUrl(TEST_FALLBACK_URL);
314 setOtherFallbackUrls(TEST_OTHER_FALLBACK_URL);
315 setFallbackSpecs(null); // Test with no fallback spec by default
316 when(mRandom.nextInt()).thenReturn(0);
317
Chiachang Wangc11639d2019-05-15 16:18:56 +0800318 // DNS probe timeout should not be defined more than half of HANDLER_TIMEOUT_MS. Otherwise,
319 // it will fail the test because of timeout expired for querying AAAA and A sequentially.
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900320 when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout)))
Chiachang Wangc11639d2019-05-15 16:18:56 +0800321 .thenReturn(200);
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900322
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900323 doAnswer((invocation) -> {
324 URL url = invocation.getArgument(0);
325 switch(url.toString()) {
326 case TEST_HTTP_URL:
327 return mHttpConnection;
328 case TEST_HTTPS_URL:
329 return mHttpsConnection;
330 case TEST_FALLBACK_URL:
331 return mFallbackConnection;
332 case TEST_OTHER_FALLBACK_URL:
333 return mOtherFallbackConnection;
334 default:
335 fail("URL not mocked: " + url.toString());
336 return null;
337 }
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900338 }).when(mCleartextDnsNetwork).openConnection(any());
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900339 when(mHttpConnection.getRequestProperties()).thenReturn(new ArrayMap<>());
340 when(mHttpsConnection.getRequestProperties()).thenReturn(new ArrayMap<>());
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900341
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900342 mFakeDns = new FakeDns();
343 mFakeDns.startMocking();
344 mFakeDns.setAnswer("*", new String[]{"2001:db8::1", "192.0.2.2"});
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900345
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900346 when(mContext.registerReceiver(any(BroadcastReceiver.class), any())).then((invocation) -> {
347 mRegisteredReceivers.add(invocation.getArgument(0));
348 return new Intent();
349 });
350
351 doAnswer((invocation) -> {
352 mRegisteredReceivers.remove(invocation.getArgument(0));
353 return null;
354 }).when(mContext).unregisterReceiver(any());
355
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900356 resetCallbacks();
357
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900358 setMinDataStallEvaluateInterval(500);
Chiachang Wang0e874792019-03-05 20:31:57 +0800359 setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900360 setValidDataStallDnsTimeThreshold(500);
361 setConsecutiveDnsTimeoutThreshold(5);
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900362
363 mCreatedNetworkMonitors = new HashSet<>();
364 mRegisteredReceivers = new HashSet<>();
365 }
366
367 @After
368 public void tearDown() {
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900369 mFakeDns.clearAll();
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900370 assertTrue(mCreatedNetworkMonitors.size() > 0);
371 // Make a local copy of mCreatedNetworkMonitors because during the iteration below,
372 // WrappedNetworkMonitor#onQuitting will delete elements from it on the handler threads.
373 WrappedNetworkMonitor[] networkMonitors = mCreatedNetworkMonitors.toArray(
374 new WrappedNetworkMonitor[0]);
375 for (WrappedNetworkMonitor nm : networkMonitors) {
376 nm.notifyNetworkDisconnected();
377 nm.awaitQuit();
378 }
379 assertEquals("NetworkMonitor still running after disconnect",
380 0, mCreatedNetworkMonitors.size());
381 assertEquals("BroadcastReceiver still registered after disconnect",
382 0, mRegisteredReceivers.size());
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900383 }
384
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900385 private void resetCallbacks() {
386 reset(mCallbacks);
387 // TODO: make this a parameterized test.
388 try {
389 when(mCallbacks.getInterfaceVersion()).thenReturn(3);
390 } catch (RemoteException e) {
391 // Can't happen as mCallbacks is a mock
392 fail("Error mocking getInterfaceVersion" + e);
393 }
394 }
395
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900396 private class WrappedNetworkMonitor extends NetworkMonitor {
397 private long mProbeTime = 0;
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900398 private final ConditionVariable mQuitCv = new ConditionVariable(false);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900399
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900400 WrappedNetworkMonitor() {
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900401 super(mContext, mCallbacks, mNetwork, mLogger, mValidationLogger,
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900402 mDependencies, mDataStallStatsUtils);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900403 }
404
405 @Override
406 protected long getLastProbeTime() {
407 return mProbeTime;
408 }
409
410 protected void setLastProbeTime(long time) {
411 mProbeTime = time;
412 }
Chiachang Wangcaa35202019-02-26 11:32:18 +0800413
414 @Override
415 protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) {
416 generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
417 }
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900418
419 @Override
420 protected void onQuitting() {
421 assertTrue(mCreatedNetworkMonitors.remove(this));
422 mQuitCv.open();
423 }
424
425 protected void awaitQuit() {
426 assertTrue("NetworkMonitor did not quit after " + HANDLER_TIMEOUT_MS + "ms",
427 mQuitCv.block(HANDLER_TIMEOUT_MS));
428 }
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900429 }
430
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900431 private WrappedNetworkMonitor makeMonitor(NetworkCapabilities nc) {
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900432 final WrappedNetworkMonitor nm = new WrappedNetworkMonitor();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900433 nm.start();
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900434 setNetworkCapabilities(nm, nc);
Chalard Jean0d0cc8c2019-05-28 16:48:32 +0900435 HandlerUtilsKt.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS);
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900436 mCreatedNetworkMonitors.add(nm);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900437 return nm;
438 }
439
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900440 private WrappedNetworkMonitor makeMeteredNetworkMonitor() {
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900441 final WrappedNetworkMonitor nm = makeMonitor(METERED_CAPABILITIES);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900442 return nm;
443 }
444
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900445 private WrappedNetworkMonitor makeNotMeteredNetworkMonitor() {
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900446 final WrappedNetworkMonitor nm = makeMonitor(NOT_METERED_CAPABILITIES);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900447 return nm;
448 }
449
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900450 private void setNetworkCapabilities(NetworkMonitor nm, NetworkCapabilities nc) {
451 nm.notifyNetworkCapabilitiesChanged(nc);
Chalard Jean0d0cc8c2019-05-28 16:48:32 +0900452 HandlerUtilsKt.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900453 }
454
455 @Test
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900456 public void testGetIntSetting() throws Exception {
457 WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor();
458
459 // No config resource, no device config. Expect to get default resource.
460 doThrow(new Resources.NotFoundException())
461 .when(mResources).getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout));
462 doAnswer(invocation -> {
463 int defaultValue = invocation.getArgument(2);
464 return defaultValue;
465 }).when(mDependencies).getDeviceConfigPropertyInt(any(),
466 eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT),
467 anyInt());
468 when(mResources.getInteger(eq(R.integer.default_captive_portal_dns_probe_timeout)))
469 .thenReturn(42);
470 assertEquals(42, wnm.getIntSetting(mContext,
471 R.integer.config_captive_portal_dns_probe_timeout,
472 NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT,
473 R.integer.default_captive_portal_dns_probe_timeout));
474
475 // Set device config. Expect to get device config.
476 when(mDependencies.getDeviceConfigPropertyInt(any(),
477 eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT), anyInt()))
478 .thenReturn(1234);
479 assertEquals(1234, wnm.getIntSetting(mContext,
480 R.integer.config_captive_portal_dns_probe_timeout,
481 NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT,
482 R.integer.default_captive_portal_dns_probe_timeout));
483
484 // Set config resource. Expect to get config resource.
485 when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout)))
486 .thenReturn(5678);
487 assertEquals(5678, wnm.getIntSetting(mContext,
488 R.integer.config_captive_portal_dns_probe_timeout,
489 NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT,
490 R.integer.default_captive_portal_dns_probe_timeout));
491 }
492
493 @Test
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900494 public void testIsCaptivePortal_HttpProbeIsPortal() throws IOException {
495 setSslException(mHttpsConnection);
496 setPortal302(mHttpConnection);
Chiachang Wang813ee472019-05-23 16:29:30 +0800497 runPortalNetworkTest(VALIDATION_RESULT_PORTAL);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900498 }
499
500 @Test
501 public void testIsCaptivePortal_HttpsProbeIsNotPortal() throws IOException {
502 setStatus(mHttpsConnection, 204);
503 setStatus(mHttpConnection, 500);
504
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900505 runNotPortalNetworkTest();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900506 }
507
508 @Test
509 public void testIsCaptivePortal_FallbackProbeIsPortal() throws IOException {
510 setSslException(mHttpsConnection);
511 setStatus(mHttpConnection, 500);
512 setPortal302(mFallbackConnection);
Chiachang Wang813ee472019-05-23 16:29:30 +0800513 runPortalNetworkTest(VALIDATION_RESULT_INVALID);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900514 }
515
516 @Test
517 public void testIsCaptivePortal_FallbackProbeIsNotPortal() throws IOException {
518 setSslException(mHttpsConnection);
519 setStatus(mHttpConnection, 500);
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900520 setStatus(mFallbackConnection, 500);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900521
522 // Fallback probe did not see portal, HTTPS failed -> inconclusive
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900523 runFailedNetworkTest();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900524 }
525
526 @Test
527 public void testIsCaptivePortal_OtherFallbackProbeIsPortal() throws IOException {
528 // Set all fallback probes but one to invalid URLs to verify they are being skipped
529 setFallbackUrl(TEST_FALLBACK_URL);
530 setOtherFallbackUrls(TEST_FALLBACK_URL + "," + TEST_OTHER_FALLBACK_URL);
531
532 setSslException(mHttpsConnection);
533 setStatus(mHttpConnection, 500);
534 setStatus(mFallbackConnection, 500);
535 setPortal302(mOtherFallbackConnection);
536
537 // TEST_OTHER_FALLBACK_URL is third
538 when(mRandom.nextInt()).thenReturn(2);
539
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900540 // First check always uses the first fallback URL: inconclusive
Chiachang Wang813ee472019-05-23 16:29:30 +0800541 final NetworkMonitor monitor = runNetworkTest(VALIDATION_RESULT_INVALID);
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900542 assertNull(mNetworkTestedRedirectUrlCaptor.getValue());
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900543 verify(mFallbackConnection, times(1)).getResponseCode();
544 verify(mOtherFallbackConnection, never()).getResponseCode();
545
546 // Second check uses the URL chosen by Random
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900547 final CaptivePortalProbeResult result = monitor.isCaptivePortal();
548 assertTrue(result.isPortal());
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900549 verify(mOtherFallbackConnection, times(1)).getResponseCode();
550 }
551
552 @Test
553 public void testIsCaptivePortal_AllProbesFailed() throws IOException {
554 setSslException(mHttpsConnection);
555 setStatus(mHttpConnection, 500);
556 setStatus(mFallbackConnection, 404);
557
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900558 runFailedNetworkTest();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900559 verify(mFallbackConnection, times(1)).getResponseCode();
560 verify(mOtherFallbackConnection, never()).getResponseCode();
561 }
562
563 @Test
564 public void testIsCaptivePortal_InvalidUrlSkipped() throws IOException {
565 setFallbackUrl("invalid");
566 setOtherFallbackUrls("otherinvalid," + TEST_OTHER_FALLBACK_URL + ",yetanotherinvalid");
567
568 setSslException(mHttpsConnection);
569 setStatus(mHttpConnection, 500);
570 setPortal302(mOtherFallbackConnection);
Chiachang Wang813ee472019-05-23 16:29:30 +0800571 runPortalNetworkTest(VALIDATION_RESULT_INVALID);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900572 verify(mOtherFallbackConnection, times(1)).getResponseCode();
573 verify(mFallbackConnection, never()).getResponseCode();
574 }
575
576 private void setupFallbackSpec() throws IOException {
577 setFallbackSpecs("http://example.com@@/@@204@@/@@"
578 + "@@,@@"
579 + TEST_OTHER_FALLBACK_URL + "@@/@@30[12]@@/@@https://(www\\.)?google.com/?.*");
580
581 setSslException(mHttpsConnection);
582 setStatus(mHttpConnection, 500);
583
584 // Use the 2nd fallback spec
585 when(mRandom.nextInt()).thenReturn(1);
586 }
587
588 @Test
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900589 public void testIsCaptivePortal_FallbackSpecIsPartial() throws IOException {
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900590 setupFallbackSpec();
591 set302(mOtherFallbackConnection, "https://www.google.com/test?q=3");
592
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900593 // HTTPS failed, fallback spec went through -> partial connectivity
Chiachang Wang813ee472019-05-23 16:29:30 +0800594 runPartialConnectivityNetworkTest(VALIDATION_RESULT_FALLBACK_PARTIAL);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900595 verify(mOtherFallbackConnection, times(1)).getResponseCode();
596 verify(mFallbackConnection, never()).getResponseCode();
597 }
598
599 @Test
600 public void testIsCaptivePortal_FallbackSpecIsPortal() throws IOException {
601 setupFallbackSpec();
602 set302(mOtherFallbackConnection, "http://login.portal.example.com");
Chiachang Wang813ee472019-05-23 16:29:30 +0800603 runPortalNetworkTest(VALIDATION_RESULT_INVALID);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900604 }
605
606 @Test
607 public void testIsCaptivePortal_IgnorePortals() throws IOException {
608 setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE);
609 setSslException(mHttpsConnection);
610 setPortal302(mHttpConnection);
611
Chiachang Wang813ee472019-05-23 16:29:30 +0800612 runNoValidationNetworkTest();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900613 }
614
615 @Test
616 public void testIsDataStall_EvaluationDisabled() {
617 setDataStallEvaluationType(0);
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900618 WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900619 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
620 assertFalse(wrappedMonitor.isDataStall());
621 }
622
623 @Test
624 public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() {
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900625 WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900626 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
Chiachang Wangcaa35202019-02-26 11:32:18 +0800627 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900628 assertTrue(wrappedMonitor.isDataStall());
629 }
630
631 @Test
632 public void testIsDataStall_EvaluationDnsOnMeteredNetwork() {
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900633 WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900634 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
635 assertFalse(wrappedMonitor.isDataStall());
636
637 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
Chiachang Wangcaa35202019-02-26 11:32:18 +0800638 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900639 assertTrue(wrappedMonitor.isDataStall());
640 }
641
642 @Test
643 public void testIsDataStall_EvaluationDnsWithDnsTimeoutCount() {
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900644 WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900645 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
646 makeDnsTimeoutEvent(wrappedMonitor, 3);
647 assertFalse(wrappedMonitor.isDataStall());
648 // Reset consecutive timeout counts.
649 makeDnsSuccessEvent(wrappedMonitor, 1);
650 makeDnsTimeoutEvent(wrappedMonitor, 2);
651 assertFalse(wrappedMonitor.isDataStall());
652
653 makeDnsTimeoutEvent(wrappedMonitor, 3);
654 assertTrue(wrappedMonitor.isDataStall());
655
656 // Set the value to larger than the default dns log size.
657 setConsecutiveDnsTimeoutThreshold(51);
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900658 wrappedMonitor = makeMeteredNetworkMonitor();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900659 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
660 makeDnsTimeoutEvent(wrappedMonitor, 50);
661 assertFalse(wrappedMonitor.isDataStall());
662
663 makeDnsTimeoutEvent(wrappedMonitor, 1);
664 assertTrue(wrappedMonitor.isDataStall());
665 }
666
667 @Test
668 public void testIsDataStall_EvaluationDnsWithDnsTimeThreshold() {
669 // Test dns events happened in valid dns time threshold.
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900670 WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900671 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
Chiachang Wangcaa35202019-02-26 11:32:18 +0800672 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900673 assertFalse(wrappedMonitor.isDataStall());
674 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
675 assertTrue(wrappedMonitor.isDataStall());
676
677 // Test dns events happened before valid dns time threshold.
678 setValidDataStallDnsTimeThreshold(0);
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900679 wrappedMonitor = makeMeteredNetworkMonitor();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900680 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
Chiachang Wangcaa35202019-02-26 11:32:18 +0800681 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900682 assertFalse(wrappedMonitor.isDataStall());
683 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
684 assertFalse(wrappedMonitor.isDataStall());
685 }
686
687 @Test
688 public void testBrokenNetworkNotValidated() throws Exception {
689 setSslException(mHttpsConnection);
690 setStatus(mHttpConnection, 500);
691 setStatus(mFallbackConnection, 404);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900692
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900693 runFailedNetworkTest();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900694 }
695
696 @Test
697 public void testNoInternetCapabilityValidated() throws Exception {
Lorenzo Colittiaa16f2d2019-06-07 18:42:34 +0900698 runNetworkTest(NO_INTERNET_CAPABILITIES, NETWORK_VALIDATION_RESULT_VALID);
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900699 verify(mCleartextDnsNetwork, never()).openConnection(any());
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900700 }
701
702 @Test
703 public void testLaunchCaptivePortalApp() throws Exception {
704 setSslException(mHttpsConnection);
705 setPortal302(mHttpConnection);
706
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900707 final NetworkMonitor nm = makeMonitor(METERED_CAPABILITIES);
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900708 nm.notifyNetworkConnected(TEST_LINK_PROPERTIES, METERED_CAPABILITIES);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900709
710 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
Remi NGUYEN VAN1e3eb372019-02-07 21:29:57 +0900711 .showProvisioningNotification(any(), any());
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900712
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900713 assertEquals(1, mRegisteredReceivers.size());
714
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900715 // Check that startCaptivePortalApp sends the expected intent.
716 nm.launchCaptivePortalApp();
717
Remi NGUYEN VANad99e542019-02-13 20:58:59 +0900718 final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
719 final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class);
720 verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1))
721 .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture());
722 final Bundle bundle = bundleCaptor.getValue();
723 final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK);
724 assertEquals(TEST_NETID, bundleNetwork.netId);
725 // network is passed both in bundle and as parameter, as the bundle is opaque to the
726 // framework and only intended for the captive portal app, but the framework needs
727 // the network to identify the right NetworkMonitor.
728 assertEquals(TEST_NETID, networkCaptor.getValue().netId);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900729
730 // Have the app report that the captive portal is dismissed, and check that we revalidate.
731 setStatus(mHttpsConnection, 204);
732 setStatus(mHttpConnection, 204);
Remi NGUYEN VANad99e542019-02-13 20:58:59 +0900733
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900734 resetCallbacks();
Remi NGUYEN VANad99e542019-02-13 20:58:59 +0900735 nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED);
Chiachang Wang813ee472019-05-23 16:29:30 +0800736 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce())
737 .notifyNetworkTested(eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP
738 | NETWORK_VALIDATION_RESULT_VALID), any());
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900739 assertEquals(0, mRegisteredReceivers.size());
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900740 }
741
Chiachang Wang8b5f84a2019-02-22 11:13:07 +0800742 @Test
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900743 public void testPrivateDnsSuccess() throws Exception {
744 setStatus(mHttpsConnection, 204);
745 setStatus(mHttpConnection, 204);
746 mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::53"});
747
748 WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor();
749 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0]));
750 wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, NOT_METERED_CAPABILITIES);
751 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
Chiachang Wang813ee472019-05-23 16:29:30 +0800752 .notifyNetworkTested(eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS),
753 eq(null));
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900754 }
755
756 @Test
757 public void testPrivateDnsResolutionRetryUpdate() throws Exception {
758 // Set a private DNS hostname that doesn't resolve and expect validation to fail.
759 mFakeDns.setAnswer("dns.google", new String[0]);
760 setStatus(mHttpsConnection, 204);
761 setStatus(mHttpConnection, 204);
762
763 WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor();
764 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0]));
765 wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, NOT_METERED_CAPABILITIES);
Chiachang Wang813ee472019-05-23 16:29:30 +0800766 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce())
767 .notifyNetworkTested(
768 eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS),
769 eq(null));
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900770
771 // Fix DNS and retry, expect validation to succeed.
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900772 resetCallbacks();
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900773 mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::1"});
774
775 wnm.forceReevaluation(Process.myUid());
Chiachang Wang813ee472019-05-23 16:29:30 +0800776 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce())
777 .notifyNetworkTested(eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS),
778 eq(null));
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900779
780 // Change configuration to an invalid DNS name, expect validation to fail.
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900781 resetCallbacks();
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900782 mFakeDns.setAnswer("dns.bad", new String[0]);
783 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.bad", new InetAddress[0]));
Chiachang Wang813ee472019-05-23 16:29:30 +0800784 // Strict mode hostname resolve fail. Expect only notification for evaluation fail. No probe
785 // notification.
Lorenzo Colittiaa16f2d2019-06-07 18:42:34 +0900786 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS))
787 .notifyNetworkTested(eq(NETWORK_VALIDATION_PROBE_DNS
788 | NETWORK_VALIDATION_PROBE_HTTPS), eq(null));
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900789
790 // Change configuration back to working again, but make private DNS not work.
791 // Expect validation to fail.
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900792 resetCallbacks();
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900793 mFakeDns.setNonBypassPrivateDnsWorking(false);
Chiachang Wang813ee472019-05-23 16:29:30 +0800794 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google",
795 new InetAddress[0]));
796 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce())
797 .notifyNetworkTested(
798 eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS),
799 eq(null));
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900800
801 // Make private DNS work again. Expect validation to succeed.
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900802 resetCallbacks();
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900803 mFakeDns.setNonBypassPrivateDnsWorking(true);
804 wnm.forceReevaluation(Process.myUid());
Chiachang Wang813ee472019-05-23 16:29:30 +0800805 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce())
806 .notifyNetworkTested(
807 eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS), eq(null));
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900808 }
809
810 @Test
Chiachang Wang8b5f84a2019-02-22 11:13:07 +0800811 public void testDataStall_StallSuspectedAndSendMetrics() throws IOException {
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900812 WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor();
Chiachang Wang8b5f84a2019-02-22 11:13:07 +0800813 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
814 makeDnsTimeoutEvent(wrappedMonitor, 5);
815 assertTrue(wrappedMonitor.isDataStall());
Chiachang Wang899d5962019-03-08 14:34:19 +0800816 verify(mDataStallStatsUtils, times(1)).write(makeEmptyDataStallDetectionStats(), any());
Chiachang Wang8b5f84a2019-02-22 11:13:07 +0800817 }
818
819 @Test
820 public void testDataStall_NoStallSuspectedAndSendMetrics() throws IOException {
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900821 WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor();
Chiachang Wang8b5f84a2019-02-22 11:13:07 +0800822 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
823 makeDnsTimeoutEvent(wrappedMonitor, 3);
824 assertFalse(wrappedMonitor.isDataStall());
Chiachang Wang899d5962019-03-08 14:34:19 +0800825 verify(mDataStallStatsUtils, never()).write(makeEmptyDataStallDetectionStats(), any());
Chiachang Wang8b5f84a2019-02-22 11:13:07 +0800826 }
Chiachang Wangcaa35202019-02-26 11:32:18 +0800827
828 @Test
829 public void testCollectDataStallMetrics() {
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900830 WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor();
Chiachang Wangcaa35202019-02-26 11:32:18 +0800831
832 when(mTelephony.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE);
833 when(mTelephony.getNetworkOperator()).thenReturn(TEST_MCCMNC);
834 when(mTelephony.getSimOperator()).thenReturn(TEST_MCCMNC);
835
836 DataStallDetectionStats.Builder stats =
837 new DataStallDetectionStats.Builder()
838 .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS)
839 .setNetworkType(NetworkCapabilities.TRANSPORT_CELLULAR)
840 .setCellData(TelephonyManager.NETWORK_TYPE_LTE /* radioType */,
841 true /* roaming */,
842 TEST_MCCMNC /* networkMccmnc */,
843 TEST_MCCMNC /* simMccmnc */,
844 CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN /* signalStrength */);
845 generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
846
847 assertEquals(wrappedMonitor.buildDataStallDetectionStats(
848 NetworkCapabilities.TRANSPORT_CELLULAR), stats.build());
849
850 when(mWifi.getConnectionInfo()).thenReturn(mWifiInfo);
851
852 stats = new DataStallDetectionStats.Builder()
853 .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS)
854 .setNetworkType(NetworkCapabilities.TRANSPORT_WIFI)
855 .setWiFiData(mWifiInfo);
856 generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
857
858 assertEquals(
859 wrappedMonitor.buildDataStallDetectionStats(NetworkCapabilities.TRANSPORT_WIFI),
860 stats.build());
861 }
862
lucaslinb0573962019-03-12 13:08:03 +0800863 @Test
864 public void testIgnoreHttpsProbe() throws Exception {
865 setSslException(mHttpsConnection);
866 setStatus(mHttpConnection, 204);
Chiachang Wang813ee472019-05-23 16:29:30 +0800867 // Expect to send HTTP, HTTPS, FALLBACK probe and evaluation result notifications to CS.
868 final NetworkMonitor nm = runNetworkTest(VALIDATION_RESULT_PARTIAL);
lucaslinb0573962019-03-12 13:08:03 +0800869
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900870 resetCallbacks();
lucaslind01ea622019-03-20 18:21:59 +0800871 nm.setAcceptPartialConnectivity();
Chiachang Wang813ee472019-05-23 16:29:30 +0800872 // Expect to update evaluation result notifications to CS.
873 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested(
874 eq(VALIDATION_RESULT_PARTIAL | NETWORK_VALIDATION_RESULT_VALID), eq(null));
lucaslinb0573962019-03-12 13:08:03 +0800875 }
876
877 @Test
878 public void testIsPartialConnectivity() throws IOException {
879 setStatus(mHttpsConnection, 500);
880 setStatus(mHttpConnection, 204);
881 setStatus(mFallbackConnection, 500);
Chiachang Wang813ee472019-05-23 16:29:30 +0800882 runPartialConnectivityNetworkTest(VALIDATION_RESULT_PARTIAL);
lucaslinb0573962019-03-12 13:08:03 +0800883
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900884 resetCallbacks();
lucaslinb0573962019-03-12 13:08:03 +0800885 setStatus(mHttpsConnection, 500);
886 setStatus(mHttpConnection, 500);
887 setStatus(mFallbackConnection, 204);
Chiachang Wang813ee472019-05-23 16:29:30 +0800888 runPartialConnectivityNetworkTest(VALIDATION_RESULT_FALLBACK_PARTIAL);
lucaslinb0573962019-03-12 13:08:03 +0800889 }
890
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900891 private void assertIpAddressArrayEquals(String[] expected, InetAddress[] actual) {
892 String[] actualStrings = new String[actual.length];
893 for (int i = 0; i < actual.length; i++) {
894 actualStrings[i] = actual[i].getHostAddress();
895 }
896 assertArrayEquals("Array of IP addresses differs", expected, actualStrings);
897 }
898
899 @Test
900 public void testSendDnsProbeWithTimeout() throws Exception {
901 WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor();
902 final int shortTimeoutMs = 200;
903
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900904 // Clear the wildcard DNS response created in setUp.
905 mFakeDns.setAnswer("*", null);
906
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900907 String[] expected = new String[]{"2001:db8::"};
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900908 mFakeDns.setAnswer("www.google.com", expected);
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900909 InetAddress[] actual = wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs);
910 assertIpAddressArrayEquals(expected, actual);
911
912 expected = new String[]{"2001:db8::", "192.0.2.1"};
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900913 mFakeDns.setAnswer("www.googleapis.com", expected);
914 actual = wnm.sendDnsProbeWithTimeout("www.googleapis.com", shortTimeoutMs);
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900915 assertIpAddressArrayEquals(expected, actual);
916
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900917 mFakeDns.setAnswer("www.google.com", new String[0]);
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900918 try {
919 wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs);
920 fail("No DNS results, expected UnknownHostException");
921 } catch (UnknownHostException e) {
922 }
923
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900924 mFakeDns.setAnswer("www.google.com", null);
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900925 try {
926 wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs);
927 fail("DNS query timed out, expected UnknownHostException");
928 } catch (UnknownHostException e) {
929 }
930 }
931
Chiachang Wang813ee472019-05-23 16:29:30 +0800932 @Test
933 public void testNotifyNetwork_WithforceReevaluation() throws Exception {
934 final NetworkMonitor nm = runValidatedNetworkTest();
935 // Verify forceReevalution will not reset the validation result but only probe result until
936 // getting the validation result.
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900937 resetCallbacks();
Chiachang Wang813ee472019-05-23 16:29:30 +0800938 setSslException(mHttpsConnection);
939 setStatus(mHttpConnection, 500);
940 setStatus(mFallbackConnection, 204);
941 nm.forceReevaluation(Process.myUid());
942 final ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class);
943 // Expect to send HTTP, HTTPs, FALLBACK and evaluation results.
Lorenzo Colittiaa16f2d2019-06-07 18:42:34 +0900944 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS))
945 .notifyNetworkTested(eq(NETWORK_VALIDATION_PROBE_DNS |
946 NETWORK_VALIDATION_PROBE_FALLBACK | NETWORK_VALIDATION_RESULT_PARTIAL), any());
Chiachang Wang813ee472019-05-23 16:29:30 +0800947 }
948
949 @Test
950 public void testEvaluationState_clearProbeResults() throws Exception {
951 final NetworkMonitor nm = runValidatedNetworkTest();
952 nm.getEvaluationState().clearProbeResults();
953 // Verify probe results are all reset and only evaluation result left.
954 assertEquals(NETWORK_VALIDATION_RESULT_VALID,
955 nm.getEvaluationState().getNetworkTestResult());
956 }
957
958 @Test
959 public void testEvaluationState_reportProbeResult() throws Exception {
960 final NetworkMonitor nm = runValidatedNetworkTest();
961
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900962 resetCallbacks();
Chiachang Wang813ee472019-05-23 16:29:30 +0800963
964 nm.reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, CaptivePortalProbeResult.SUCCESS);
965 // Verify result should be appended and notifyNetworkTested callback is triggered once.
966 assertEquals(nm.getEvaluationState().getNetworkTestResult(),
967 VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_HTTP);
Chiachang Wang813ee472019-05-23 16:29:30 +0800968
969 nm.reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, CaptivePortalProbeResult.FAILED);
970 // Verify DNS probe result should not be cleared.
971 assertTrue((nm.getEvaluationState().getNetworkTestResult() & NETWORK_VALIDATION_PROBE_DNS)
972 == NETWORK_VALIDATION_PROBE_DNS);
973 }
974
975 @Test
976 public void testEvaluationState_reportEvaluationResult() throws Exception {
977 final NetworkMonitor nm = runValidatedNetworkTest();
978
979 nm.getEvaluationState().reportEvaluationResult(NETWORK_VALIDATION_RESULT_PARTIAL,
980 null /* redirectUrl */);
981 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested(
982 eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS
983 | NETWORK_VALIDATION_RESULT_PARTIAL), eq(null));
984
985 nm.getEvaluationState().reportEvaluationResult(
986 NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_PARTIAL,
987 null /* redirectUrl */);
988 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested(
989 eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_PARTIAL), eq(null));
990
991 nm.getEvaluationState().reportEvaluationResult(VALIDATION_RESULT_INVALID,
992 TEST_REDIRECT_URL);
993 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested(
994 eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS),
995 eq(TEST_REDIRECT_URL));
996 }
997
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900998 private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
999 for (int i = 0; i < count; i++) {
1000 wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
1001 RETURN_CODE_DNS_TIMEOUT);
1002 }
1003 }
1004
1005 private void makeDnsSuccessEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
1006 for (int i = 0; i < count; i++) {
1007 wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
1008 RETURN_CODE_DNS_SUCCESS);
1009 }
1010 }
1011
Chiachang Wang899d5962019-03-08 14:34:19 +08001012 private DataStallDetectionStats makeEmptyDataStallDetectionStats() {
1013 return new DataStallDetectionStats.Builder().build();
1014 }
1015
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001016 private void setDataStallEvaluationType(int type) {
Chiachang Wang9a87f802019-04-08 19:06:21 +08001017 when(mDependencies.getDeviceConfigPropertyInt(any(),
1018 eq(CONFIG_DATA_STALL_EVALUATION_TYPE), anyInt())).thenReturn(type);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001019 }
1020
1021 private void setMinDataStallEvaluateInterval(int time) {
Chiachang Wang9a87f802019-04-08 19:06:21 +08001022 when(mDependencies.getDeviceConfigPropertyInt(any(),
1023 eq(CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL), anyInt())).thenReturn(time);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001024 }
1025
1026 private void setValidDataStallDnsTimeThreshold(int time) {
Chiachang Wang9a87f802019-04-08 19:06:21 +08001027 when(mDependencies.getDeviceConfigPropertyInt(any(),
1028 eq(CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD), anyInt())).thenReturn(time);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001029 }
1030
1031 private void setConsecutiveDnsTimeoutThreshold(int num) {
Chiachang Wang9a87f802019-04-08 19:06:21 +08001032 when(mDependencies.getDeviceConfigPropertyInt(any(),
1033 eq(CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD), anyInt())).thenReturn(num);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001034 }
1035
1036 private void setFallbackUrl(String url) {
1037 when(mDependencies.getSetting(any(),
1038 eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL), any())).thenReturn(url);
1039 }
1040
1041 private void setOtherFallbackUrls(String urls) {
Chiachang Wang79a6da32019-04-17 17:00:54 +08001042 when(mDependencies.getDeviceConfigProperty(any(),
1043 eq(CAPTIVE_PORTAL_OTHER_FALLBACK_URLS), any())).thenReturn(urls);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001044 }
1045
1046 private void setFallbackSpecs(String specs) {
Chiachang Wang79a6da32019-04-17 17:00:54 +08001047 when(mDependencies.getDeviceConfigProperty(any(),
1048 eq(CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001049 }
1050
1051 private void setCaptivePortalMode(int mode) {
1052 when(mDependencies.getSetting(any(),
1053 eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())).thenReturn(mode);
1054 }
1055
Chiachang Wang813ee472019-05-23 16:29:30 +08001056 private void runPortalNetworkTest(int result) {
Lorenzo Colittiaa16f2d2019-06-07 18:42:34 +09001057 runNetworkTest(result);
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +09001058 assertEquals(1, mRegisteredReceivers.size());
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09001059 assertNotNull(mNetworkTestedRedirectUrlCaptor.getValue());
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001060 }
1061
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09001062 private void runNotPortalNetworkTest() {
Chiachang Wang813ee472019-05-23 16:29:30 +08001063 runNetworkTest(VALIDATION_RESULT_VALID);
1064 assertEquals(0, mRegisteredReceivers.size());
1065 assertNull(mNetworkTestedRedirectUrlCaptor.getValue());
1066 }
1067
1068 private void runNoValidationNetworkTest() {
1069 runNetworkTest(NETWORK_VALIDATION_RESULT_VALID);
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +09001070 assertEquals(0, mRegisteredReceivers.size());
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09001071 assertNull(mNetworkTestedRedirectUrlCaptor.getValue());
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001072 }
1073
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09001074 private void runFailedNetworkTest() {
Chiachang Wang813ee472019-05-23 16:29:30 +08001075 runNetworkTest(VALIDATION_RESULT_INVALID);
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +09001076 assertEquals(0, mRegisteredReceivers.size());
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09001077 assertNull(mNetworkTestedRedirectUrlCaptor.getValue());
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001078 }
1079
Chiachang Wang813ee472019-05-23 16:29:30 +08001080 private void runPartialConnectivityNetworkTest(int result) {
1081 runNetworkTest(result);
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +09001082 assertEquals(0, mRegisteredReceivers.size());
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09001083 assertNull(mNetworkTestedRedirectUrlCaptor.getValue());
1084 }
1085
Chiachang Wang813ee472019-05-23 16:29:30 +08001086 private NetworkMonitor runValidatedNetworkTest() throws Exception {
1087 setStatus(mHttpsConnection, 204);
1088 setStatus(mHttpConnection, 204);
1089 // Expect to send HTTPs and evaluation results.
1090 return runNetworkTest(VALIDATION_RESULT_VALID);
1091 }
1092
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09001093 private NetworkMonitor runNetworkTest(int testResult) {
Lorenzo Colittiaa16f2d2019-06-07 18:42:34 +09001094 return runNetworkTest(METERED_CAPABILITIES, testResult);
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09001095 }
1096
Lorenzo Colittiaa16f2d2019-06-07 18:42:34 +09001097 private NetworkMonitor runNetworkTest(NetworkCapabilities nc, int testResult) {
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09001098 final NetworkMonitor monitor = makeMonitor(nc);
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09001099 monitor.notifyNetworkConnected(TEST_LINK_PROPERTIES, nc);
1100 try {
Lorenzo Colittiaa16f2d2019-06-07 18:42:34 +09001101 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS))
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09001102 .notifyNetworkTested(eq(testResult), mNetworkTestedRedirectUrlCaptor.capture());
1103 } catch (RemoteException e) {
1104 fail("Unexpected exception: " + e);
1105 }
Chalard Jean0d0cc8c2019-05-28 16:48:32 +09001106 HandlerUtilsKt.waitForIdle(monitor.getHandler(), HANDLER_TIMEOUT_MS);
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09001107
1108 return monitor;
lucaslinb0573962019-03-12 13:08:03 +08001109 }
1110
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001111 private void setSslException(HttpURLConnection connection) throws IOException {
1112 doThrow(new SSLHandshakeException("Invalid cert")).when(connection).getResponseCode();
1113 }
1114
1115 private void set302(HttpURLConnection connection, String location) throws IOException {
1116 setStatus(connection, 302);
1117 doReturn(location).when(connection).getHeaderField(LOCATION_HEADER);
1118 }
1119
1120 private void setPortal302(HttpURLConnection connection) throws IOException {
1121 set302(connection, "http://login.example.com");
1122 }
1123
1124 private void setStatus(HttpURLConnection connection, int status) throws IOException {
1125 doReturn(status).when(connection).getResponseCode();
1126 }
Chiachang Wangcaa35202019-02-26 11:32:18 +08001127
1128 private void generateTimeoutDnsEvent(DataStallDetectionStats.Builder stats, int num) {
1129 for (int i = 0; i < num; i++) {
1130 stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, 123456789 /* timeMs */);
1131 }
1132 }
Chiachang Wang813ee472019-05-23 16:29:30 +08001133
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001134}
1135