blob: 606d243a5105904ffe56057ede7ebff968a5e182 [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 Wangeb619222019-07-03 20:52:08 +080020import static android.net.DnsResolver.TYPE_A;
21import static android.net.DnsResolver.TYPE_AAAA;
Chiachang Wang813ee472019-05-23 16:29:30 +080022import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
23import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK;
24import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP;
25import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS;
26import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
27import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
Cody Kesting78156e42020-08-03 18:19:56 -070028import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_SKIPPED;
Chiachang Wang813ee472019-05-23 16:29:30 +080029import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090030import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
lucasline8d54032020-04-29 02:35:19 +080031import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
Pavan Kumar M2fbc8462021-04-28 11:20:25 +053032import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
Remi NGUYEN VAN1b7a0632021-07-07 17:54:09 +090033import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
Pavan Kumar M2fbc8462021-04-28 11:20:25 +053034import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
Remi NGUYEN VAN1b7a0632021-07-07 17:54:09 +090035import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
lucasline8d54032020-04-29 02:35:19 +080036import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
37import static android.net.NetworkCapabilities.TRANSPORT_VPN;
38import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
Chiachang Wang717099f2020-03-23 11:31:39 +080039import static android.net.metrics.ValidationProbeEvent.PROBE_HTTP;
Chiachang Wang9a87f802019-04-08 19:06:21 +080040import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD;
41import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_EVALUATION_TYPE;
42import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL;
Chiachang Wanga5716bf2019-11-20 16:13:07 +080043import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_TCP_POLLING_INTERVAL;
Chiachang Wang9a87f802019-04-08 19:06:21 +080044import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD;
45import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS;
Chiachang Wanga5716bf2019-11-20 16:13:07 +080046import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_TCP;
Chiachang Wang9af1cb52020-05-20 15:14:46 +080047import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_EVALUATION_TYPES;
Chiachang Wang79a6da32019-04-17 17:00:54 +080048import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS;
49import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS;
50import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS;
Chiachang Wang5dbe6852020-04-10 09:02:45 +080051import static android.net.util.NetworkStackUtils.DEFAULT_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT;
Chiachang Wang26a626a2020-03-02 17:41:58 +080052import static android.net.util.NetworkStackUtils.DISMISS_PORTAL_IN_VALIDATED_NETWORK;
Yeo4915faf2019-08-26 14:53:23 +090053import static android.net.util.NetworkStackUtils.DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION;
Remi NGUYEN VAN943d6342020-03-24 18:22:34 +090054import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL;
55import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL;
56import static android.net.util.NetworkStackUtils.TEST_URL_EXPIRATION_TIME;
57import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090058
Chiachang Wangeb619222019-07-03 20:52:08 +080059import static com.android.networkstack.util.DnsUtils.PRIVATE_DNS_PROBE_HOST_SUFFIX;
Remi NGUYEN VAN2f187302020-05-21 14:35:50 +090060import static com.android.server.connectivity.NetworkMonitor.INITIAL_REEVALUATE_DELAY_MS;
Remi NGUYEN VAN2d909a72019-12-24 18:15:52 +090061import static com.android.server.connectivity.NetworkMonitor.extractCharset;
Chiachang Wangeb619222019-07-03 20:52:08 +080062
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090063import static junit.framework.Assert.assertEquals;
64import static junit.framework.Assert.assertFalse;
65
Lorenzo Colitti171cfd22019-04-18 13:44:32 +090066import static org.junit.Assert.assertArrayEquals;
lucaslin9073e7b2020-04-15 23:02:14 +080067import static org.junit.Assert.assertNotEquals;
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +090068import static org.junit.Assert.assertNotNull;
69import static org.junit.Assert.assertNull;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090070import static org.junit.Assert.assertTrue;
71import static org.junit.Assert.fail;
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +090072import static org.junit.Assume.assumeFalse;
73import static org.junit.Assume.assumeTrue;
Chiachang Wang26a626a2020-03-02 17:41:58 +080074import static org.mockito.ArgumentMatchers.anyBoolean;
Cody Kesting176bce72020-01-20 18:09:59 -080075import static org.mockito.ArgumentMatchers.argThat;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090076import static org.mockito.ArgumentMatchers.eq;
Remi NGUYEN VANebb5a9f2020-05-20 19:11:56 +090077import static org.mockito.Mockito.after;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090078import static org.mockito.Mockito.any;
79import static org.mockito.Mockito.anyInt;
Remi NGUYEN VANeb5bfa92020-05-01 10:43:12 +090080import static org.mockito.Mockito.atLeastOnce;
81import static org.mockito.Mockito.atMost;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090082import static org.mockito.Mockito.doAnswer;
83import static org.mockito.Mockito.doReturn;
84import static org.mockito.Mockito.doThrow;
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +090085import static org.mockito.Mockito.mock;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090086import static org.mockito.Mockito.never;
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +090087import static org.mockito.Mockito.reset;
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +090088import static org.mockito.Mockito.spy;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +090089import static org.mockito.Mockito.timeout;
90import static org.mockito.Mockito.times;
91import static org.mockito.Mockito.verify;
92import static org.mockito.Mockito.when;
93
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +090094import static java.lang.System.currentTimeMillis;
95import static java.util.Collections.singletonList;
Lorenzo Colittib891b5e2020-02-05 16:48:09 +090096import static java.util.stream.Collectors.toList;
97
Chiachang Wangcaa35202019-02-26 11:32:18 +080098import android.annotation.NonNull;
Lorenzo Colitti5cba6b62021-06-28 23:08:04 +090099import android.annotation.SuppressLint;
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900100import android.content.BroadcastReceiver;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900101import android.content.Context;
lucaslin9b4dfab2019-12-17 23:06:12 +0800102import android.content.ContextWrapper;
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900103import android.content.Intent;
lucaslin9b4dfab2019-12-17 23:06:12 +0800104import android.content.pm.PackageManager;
105import android.content.res.Configuration;
Niklas Lindgren0c904882018-12-07 11:08:04 +0100106import android.content.res.Resources;
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +0900107import android.net.CaptivePortalData;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900108import android.net.ConnectivityManager;
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +0900109import android.net.DataStallReportParcelable;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900110import android.net.DnsResolver;
Chiachang Wangcded6ce2019-12-18 17:27:57 +0800111import android.net.INetd;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900112import android.net.INetworkMonitorCallbacks;
Yeo4915faf2019-08-26 14:53:23 +0900113import android.net.InetAddresses;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900114import android.net.LinkProperties;
115import android.net.Network;
116import android.net.NetworkCapabilities;
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +0900117import android.net.NetworkTestResultParcelable;
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +0900118import android.net.Uri;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900119import android.net.captiveportal.CaptivePortalProbeResult;
120import android.net.metrics.IpConnectivityLog;
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900121import android.net.shared.PrivateDnsConfig;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900122import android.net.util.SharedLog;
Chiachang Wangcaa35202019-02-26 11:32:18 +0800123import android.net.wifi.WifiInfo;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900124import android.net.wifi.WifiManager;
Remi NGUYEN VAN7fc260a2020-02-03 13:33:58 +0900125import android.os.Build;
Remi NGUYEN VANad99e542019-02-13 20:58:59 +0900126import android.os.Bundle;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900127import android.os.ConditionVariable;
128import android.os.Handler;
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +0900129import android.os.IBinder;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900130import android.os.Looper;
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900131import android.os.Process;
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900132import android.os.RemoteException;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900133import android.os.SystemClock;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900134import android.provider.Settings;
lucaslin9b4dfab2019-12-17 23:06:12 +0800135import android.telephony.CellIdentityGsm;
136import android.telephony.CellIdentityLte;
137import android.telephony.CellInfo;
138import android.telephony.CellInfoGsm;
139import android.telephony.CellInfoLte;
Chiachang Wangcaa35202019-02-26 11:32:18 +0800140import android.telephony.CellSignalStrength;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900141import android.telephony.TelephonyManager;
142import android.util.ArrayMap;
143
Brett Chabot189c5982019-02-26 14:52:33 -0800144import androidx.test.filters.SmallTest;
145import androidx.test.runner.AndroidJUnit4;
146
Remi NGUYEN VAN89cd0262019-12-29 22:46:00 +0900147import com.android.networkstack.NetworkStackNotifier;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900148import com.android.networkstack.R;
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +0900149import com.android.networkstack.apishim.CaptivePortalDataShimImpl;
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +0900150import com.android.networkstack.apishim.ConstantsShim;
Hai Shalome8fe1232021-02-08 11:47:15 -0800151import com.android.networkstack.apishim.NetworkInformationShimImpl;
152import com.android.networkstack.apishim.common.CaptivePortalDataShim;
153import com.android.networkstack.apishim.common.NetworkInformationShim;
Remi NGUYEN VAN046bfa72020-05-14 14:32:03 +0900154import com.android.networkstack.apishim.common.ShimUtils;
Hai Shalome8fe1232021-02-08 11:47:15 -0800155import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
Chiachang Wang80242272019-04-11 21:24:28 +0800156import com.android.networkstack.metrics.DataStallDetectionStats;
157import com.android.networkstack.metrics.DataStallStatsUtils;
Chiachang Wanga5716bf2019-11-20 16:13:07 +0800158import com.android.networkstack.netlink.TcpSocketTracker;
Remi NGUYEN VAN89cd0262019-12-29 22:46:00 +0900159import com.android.server.NetworkStackService.NetworkStackServiceManager;
Chiachang Wang9af1cb52020-05-20 15:14:46 +0800160import com.android.server.connectivity.nano.CellularData;
161import com.android.server.connectivity.nano.DnsEvent;
162import com.android.server.connectivity.nano.WifiData;
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +0900163import com.android.testutils.DevSdkIgnoreRule;
Remi NGUYEN VANa7500732021-05-27 14:35:18 +0900164import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +0900165import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
Chalard Jean23a06302020-06-26 00:41:00 +0900166import com.android.testutils.HandlerUtils;
Chiachang Wang80242272019-04-11 21:24:28 +0800167
Chiachang Wang9af1cb52020-05-20 15:14:46 +0800168import com.google.protobuf.nano.MessageNano;
169
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +0900170import junit.framework.AssertionFailedError;
171
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900172import org.junit.After;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900173import org.junit.Before;
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +0900174import org.junit.Rule;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900175import org.junit.Test;
176import org.junit.runner.RunWith;
177import org.mockito.ArgumentCaptor;
178import org.mockito.Mock;
179import org.mockito.MockitoAnnotations;
180import org.mockito.Spy;
Chiachang Wangddb7da62019-06-03 15:50:53 +0800181import org.mockito.invocation.InvocationOnMock;
182import org.mockito.stubbing.Answer;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900183
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +0900184import java.io.ByteArrayInputStream;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900185import java.io.IOException;
Remi NGUYEN VAN2d909a72019-12-24 18:15:52 +0900186import java.io.InputStream;
Remi NGUYEN VAN7fc260a2020-02-03 13:33:58 +0900187import java.lang.reflect.Constructor;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900188import java.net.HttpURLConnection;
Yeo4915faf2019-08-26 14:53:23 +0900189import java.net.Inet6Address;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900190import java.net.InetAddress;
191import java.net.URL;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900192import java.net.UnknownHostException;
Remi NGUYEN VAN2d909a72019-12-24 18:15:52 +0900193import java.nio.charset.Charset;
194import java.nio.charset.StandardCharsets;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900195import java.util.ArrayList;
Chiachang Wangeb619222019-07-03 20:52:08 +0800196import java.util.Arrays;
Nathan Harold61b39232020-01-21 16:48:25 -0800197import java.util.Collections;
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +0900198import java.util.HashMap;
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900199import java.util.HashSet;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900200import java.util.List;
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +0900201import java.util.Map;
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +0900202import java.util.Objects;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900203import java.util.Random;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900204import java.util.concurrent.Executor;
Remi NGUYEN VAN943d6342020-03-24 18:22:34 +0900205import java.util.concurrent.TimeUnit;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900206
207import javax.net.ssl.SSLHandshakeException;
208
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900209@RunWith(AndroidJUnit4.class)
210@SmallTest
Lorenzo Colitti5cba6b62021-06-28 23:08:04 +0900211@SuppressLint("NewApi") // Uses hidden APIs, which the linter would identify as missing APIs.
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900212public class NetworkMonitorTest {
213 private static final String LOCATION_HEADER = "location";
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +0900214 private static final String CONTENT_TYPE_HEADER = "Content-Type";
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900215
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +0900216 @Rule
217 public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
218
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900219 private @Mock Context mContext;
lucaslin9b4dfab2019-12-17 23:06:12 +0800220 private @Mock Configuration mConfiguration;
Niklas Lindgren0c904882018-12-07 11:08:04 +0100221 private @Mock Resources mResources;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900222 private @Mock IpConnectivityLog mLogger;
223 private @Mock SharedLog mValidationLogger;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900224 private @Mock DnsResolver mDnsResolver;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900225 private @Mock ConnectivityManager mCm;
226 private @Mock TelephonyManager mTelephony;
227 private @Mock WifiManager mWifi;
Remi NGUYEN VAN89cd0262019-12-29 22:46:00 +0900228 private @Mock NetworkStackServiceManager mServiceManager;
229 private @Mock NetworkStackNotifier mNotifier;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900230 private @Mock HttpURLConnection mHttpConnection;
Chiachang Wang80be4412020-04-14 11:52:10 +0800231 private @Mock HttpURLConnection mOtherHttpConnection1;
232 private @Mock HttpURLConnection mOtherHttpConnection2;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900233 private @Mock HttpURLConnection mHttpsConnection;
Chiachang Wang80be4412020-04-14 11:52:10 +0800234 private @Mock HttpURLConnection mOtherHttpsConnection1;
235 private @Mock HttpURLConnection mOtherHttpsConnection2;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900236 private @Mock HttpURLConnection mFallbackConnection;
237 private @Mock HttpURLConnection mOtherFallbackConnection;
Remi NGUYEN VAN943d6342020-03-24 18:22:34 +0900238 private @Mock HttpURLConnection mTestOverriddenUrlConnection;
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +0900239 private @Mock HttpURLConnection mCapportApiConnection;
lucasline8d54032020-04-29 02:35:19 +0800240 private @Mock HttpURLConnection mSpeedTestConnection;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900241 private @Mock Random mRandom;
242 private @Mock NetworkMonitor.Dependencies mDependencies;
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +0900243 // Mockito can't create a mock of INetworkMonitorCallbacks on Q because it can't find
244 // CaptivePortalData on notifyCaptivePortalDataChanged. Use a spy on a mock IBinder instead.
245 private INetworkMonitorCallbacks mCallbacks = spy(
246 INetworkMonitorCallbacks.Stub.asInterface(mock(IBinder.class)));
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900247 private @Spy Network mCleartextDnsNetwork = new Network(TEST_NETID);
248 private @Mock Network mNetwork;
Chiachang Wang8b5f84a2019-02-22 11:13:07 +0800249 private @Mock DataStallStatsUtils mDataStallStatsUtils;
Chiachang Wanga5716bf2019-11-20 16:13:07 +0800250 private @Mock TcpSocketTracker.Dependencies mTstDependencies;
Chiachang Wangcded6ce2019-12-18 17:27:57 +0800251 private @Mock INetd mNetd;
Chiachang Wang4336b912019-11-26 15:39:22 +0800252 private @Mock TcpSocketTracker mTst;
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900253 private HashSet<WrappedNetworkMonitor> mCreatedNetworkMonitors;
254 private HashSet<BroadcastReceiver> mRegisteredReceivers;
Chiachang Wang24820992020-03-23 21:16:36 +0800255 private @Mock Context mMccContext;
256 private @Mock Resources mMccResource;
Chiachang Wang9af1cb52020-05-20 15:14:46 +0800257 private @Mock WifiInfo mWifiInfo;
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900258
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900259 private static final int TEST_NETID = 4242;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900260 private static final String TEST_HTTP_URL = "http://www.google.com/gen_204";
Chiachang Wang80be4412020-04-14 11:52:10 +0800261 private static final String TEST_HTTP_OTHER_URL1 = "http://other1.google.com/gen_204";
262 private static final String TEST_HTTP_OTHER_URL2 = "http://other2.google.com/gen_204";
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900263 private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204";
Chiachang Wang80be4412020-04-14 11:52:10 +0800264 private static final String TEST_HTTPS_OTHER_URL1 = "https://other1.google.com/gen_204";
265 private static final String TEST_HTTPS_OTHER_URL2 = "https://other2.google.com/gen_204";
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900266 private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204";
267 private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204";
Remi NGUYEN VAN943d6342020-03-24 18:22:34 +0900268 private static final String TEST_INVALID_OVERRIDE_URL = "https://override.example.com/test";
269 private static final String TEST_OVERRIDE_URL = "http://localhost:12345/test";
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +0900270 private static final String TEST_CAPPORT_API_URL = "https://capport.example.com/api";
271 private static final String TEST_LOGIN_URL = "https://testportal.example.com/login";
272 private static final String TEST_VENUE_INFO_URL = "https://venue.example.com/info";
lucasline8d54032020-04-29 02:35:19 +0800273 private static final String TEST_SPEED_TEST_URL = "https://speedtest.example.com";
Chiachang Wang220428b2020-05-29 10:03:41 +0800274 private static final String TEST_RELATIVE_URL = "/test/relative/gen_204";
Chiachang Wangcaa35202019-02-26 11:32:18 +0800275 private static final String TEST_MCCMNC = "123456";
Hai Shalome8fe1232021-02-08 11:47:15 -0800276 private static final String TEST_FRIENDLY_NAME = "Friendly Name";
Chiachang Wang80be4412020-04-14 11:52:10 +0800277 private static final String[] TEST_HTTP_URLS = {TEST_HTTP_OTHER_URL1, TEST_HTTP_OTHER_URL2};
278 private static final String[] TEST_HTTPS_URLS = {TEST_HTTPS_OTHER_URL1, TEST_HTTPS_OTHER_URL2};
Chiachang Wang9af1cb52020-05-20 15:14:46 +0800279 private static final int TEST_TCP_FAIL_RATE = 99;
280 private static final int TEST_TCP_PACKET_COUNT = 50;
281 private static final long TEST_ELAPSED_TIME_MS = 123456789L;
282 private static final int TEST_SIGNAL_STRENGTH = -100;
Chiachang Wang813ee472019-05-23 16:29:30 +0800283 private static final int VALIDATION_RESULT_INVALID = 0;
284 private static final int VALIDATION_RESULT_PORTAL = 0;
285 private static final String TEST_REDIRECT_URL = "android.com";
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +0900286 private static final int PROBES_PRIVDNS_VALID = NETWORK_VALIDATION_PROBE_DNS
lucaslin2ce7dcc2019-10-22 16:59:39 +0800287 | NETWORK_VALIDATION_PROBE_HTTPS | NETWORK_VALIDATION_PROBE_PRIVDNS;
Chiachang Wang813ee472019-05-23 16:29:30 +0800288
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900289 private static final int RETURN_CODE_DNS_SUCCESS = 0;
290 private static final int RETURN_CODE_DNS_TIMEOUT = 255;
Chiachang Wangcaa35202019-02-26 11:32:18 +0800291 private static final int DEFAULT_DNS_TIMEOUT_THRESHOLD = 5;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900292
293 private static final int HANDLER_TIMEOUT_MS = 1000;
Chiachang Wang4fcbe912021-04-26 13:02:22 +0800294 private static final int TEST_MIN_STALL_EVALUATE_INTERVAL_MS = 500;
295 private static final int STALL_EXPECTED_LAST_PROBE_TIME_MS =
296 TEST_MIN_STALL_EVALUATE_INTERVAL_MS + HANDLER_TIMEOUT_MS;
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900297 private static final LinkProperties TEST_LINK_PROPERTIES = new LinkProperties();
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +0900298
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +0900299 // Cannot have a static member for the LinkProperties with captive portal API information, as
300 // the initializer would crash on Q (the members in LinkProperties were introduced in R).
301 private static LinkProperties makeCapportLPs() {
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +0900302 final LinkProperties lp = new LinkProperties(TEST_LINK_PROPERTIES);
303 lp.setCaptivePortalApiUrl(Uri.parse(TEST_CAPPORT_API_URL));
304 return lp;
305 }
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900306
Chiachang Wangdaf983b2020-05-07 19:20:52 +0800307 private static final NetworkCapabilities CELL_METERED_CAPABILITIES = new NetworkCapabilities()
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900308 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
309 .addCapability(NET_CAPABILITY_INTERNET);
310
Chiachang Wangdaf983b2020-05-07 19:20:52 +0800311 private static final NetworkCapabilities CELL_NOT_METERED_CAPABILITIES =
312 new NetworkCapabilities()
313 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
314 .addCapability(NET_CAPABILITY_INTERNET)
315 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900316
Chiachang Wang9af1cb52020-05-20 15:14:46 +0800317 private static final NetworkCapabilities WIFI_NOT_METERED_CAPABILITIES =
318 new NetworkCapabilities()
319 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
320 .addCapability(NET_CAPABILITY_INTERNET)
321 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
322
Chiachang Wangdaf983b2020-05-07 19:20:52 +0800323 private static final NetworkCapabilities CELL_NO_INTERNET_CAPABILITIES =
324 new NetworkCapabilities().addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900325
Pavan Kumar M2fbc8462021-04-28 11:20:25 +0530326 private static final NetworkCapabilities WIFI_OEM_PAID_CAPABILITIES =
327 new NetworkCapabilities()
328 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
329 .addCapability(NET_CAPABILITY_INTERNET)
330 .addCapability(NET_CAPABILITY_NOT_METERED)
331 .addCapability(NET_CAPABILITY_OEM_PAID)
332 .removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
333
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900334 /**
335 * Fakes DNS responses.
336 *
337 * Allows test methods to configure the IP addresses that will be resolved by
338 * Network#getAllByName and by DnsResolver#query.
339 */
340 class FakeDns {
Chiachang Wangeb619222019-07-03 20:52:08 +0800341 /** Data class to record the Dns entry. */
342 class DnsEntry {
343 final String mHostname;
344 final int mType;
345 final List<InetAddress> mAddresses;
346 DnsEntry(String host, int type, List<InetAddress> addr) {
347 mHostname = host;
348 mType = type;
349 mAddresses = addr;
350 }
351 // Full match or partial match that target host contains the entry hostname to support
352 // random private dns probe hostname.
353 private boolean matches(String hostname, int type) {
354 return hostname.endsWith(mHostname) && type == mType;
355 }
356 }
357 private final ArrayList<DnsEntry> mAnswers = new ArrayList<DnsEntry>();
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900358 private boolean mNonBypassPrivateDnsWorking = true;
359
360 /** Whether DNS queries on mNonBypassPrivateDnsWorking should succeed. */
361 private void setNonBypassPrivateDnsWorking(boolean working) {
362 mNonBypassPrivateDnsWorking = working;
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900363 }
364
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900365 /** Clears all DNS entries. */
366 private synchronized void clearAll() {
367 mAnswers.clear();
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900368 }
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900369
Chiachang Wangeb619222019-07-03 20:52:08 +0800370 /** Returns the answer for a given name and type on the given mock network. */
371 private synchronized List<InetAddress> getAnswer(Object mock, String hostname, int type) {
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900372 if (mock == mNetwork && !mNonBypassPrivateDnsWorking) {
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900373 return null;
374 }
Chiachang Wangeb619222019-07-03 20:52:08 +0800375
Lorenzo Colittib891b5e2020-02-05 16:48:09 +0900376 return mAnswers.stream().filter(e -> e.matches(hostname, type))
377 .map(answer -> answer.mAddresses).findFirst().orElse(null);
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900378 }
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900379
Chiachang Wangeb619222019-07-03 20:52:08 +0800380 /** Sets the answer for a given name and type. */
381 private synchronized void setAnswer(String hostname, String[] answer, int type)
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900382 throws UnknownHostException {
Chiachang Wangeb619222019-07-03 20:52:08 +0800383 DnsEntry record = new DnsEntry(hostname, type, generateAnswer(answer));
384 // Remove the existing one.
385 mAnswers.removeIf(entry -> entry.matches(hostname, type));
386 // Add or replace a new record.
387 mAnswers.add(record);
388 }
389
390 private List<InetAddress> generateAnswer(String[] answer) {
391 if (answer == null) return new ArrayList<>();
Lorenzo Colittib891b5e2020-02-05 16:48:09 +0900392 return Arrays.stream(answer).map(addr -> InetAddress.parseNumericAddress(addr))
393 .collect(toList());
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900394 }
395
396 /** Simulates a getAllByName call for the specified name on the specified mock network. */
397 private InetAddress[] getAllByName(Object mock, String hostname)
398 throws UnknownHostException {
Chiachang Wangeb619222019-07-03 20:52:08 +0800399 List<InetAddress> answer = queryAllTypes(mock, hostname);
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900400 if (answer == null || answer.size() == 0) {
401 throw new UnknownHostException(hostname);
402 }
403 return answer.toArray(new InetAddress[0]);
404 }
405
Chiachang Wangeb619222019-07-03 20:52:08 +0800406 // Regardless of the type, depends on what the responses contained in the network.
407 private List<InetAddress> queryAllTypes(Object mock, String hostname) {
408 List<InetAddress> answer = new ArrayList<>();
409 addAllIfNotNull(answer, getAnswer(mock, hostname, TYPE_A));
410 addAllIfNotNull(answer, getAnswer(mock, hostname, TYPE_AAAA));
411 return answer;
412 }
413
414 private void addAllIfNotNull(List<InetAddress> list, List<InetAddress> c) {
415 if (c != null) {
416 list.addAll(c);
417 }
418 }
419
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900420 /** Starts mocking DNS queries. */
421 private void startMocking() throws UnknownHostException {
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900422 // Queries on mNetwork using getAllByName.
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900423 doAnswer(invocation -> {
424 return getAllByName(invocation.getMock(), invocation.getArgument(0));
425 }).when(mNetwork).getAllByName(any());
426
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900427 // Queries on mCleartextDnsNetwork using DnsResolver#query.
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900428 doAnswer(invocation -> {
Chiachang Wangddb7da62019-06-03 15:50:53 +0800429 return mockQuery(invocation, 1 /* posHostname */, 3 /* posExecutor */,
Chiachang Wangeb619222019-07-03 20:52:08 +0800430 5 /* posCallback */, -1 /* posType */);
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900431 }).when(mDnsResolver).query(any(), any(), anyInt(), any(), any(), any());
Chiachang Wang1c67f4e2019-05-09 21:28:47 +0800432
Chiachang Wangddb7da62019-06-03 15:50:53 +0800433 // Queries on mCleartextDnsNetwork using DnsResolver#query with QueryType.
Chiachang Wang1c67f4e2019-05-09 21:28:47 +0800434 doAnswer(invocation -> {
Chiachang Wangddb7da62019-06-03 15:50:53 +0800435 return mockQuery(invocation, 1 /* posHostname */, 4 /* posExecutor */,
Chiachang Wangeb619222019-07-03 20:52:08 +0800436 6 /* posCallback */, 2 /* posType */);
Chiachang Wang1c67f4e2019-05-09 21:28:47 +0800437 }).when(mDnsResolver).query(any(), any(), anyInt(), anyInt(), any(), any(), any());
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900438 }
Chiachang Wangddb7da62019-06-03 15:50:53 +0800439
440 // Mocking queries on DnsResolver#query.
441 private Answer mockQuery(InvocationOnMock invocation, int posHostname, int posExecutor,
Chiachang Wangeb619222019-07-03 20:52:08 +0800442 int posCallback, int posType) {
Chiachang Wangddb7da62019-06-03 15:50:53 +0800443 String hostname = (String) invocation.getArgument(posHostname);
444 Executor executor = (Executor) invocation.getArgument(posExecutor);
445 DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(posCallback);
Chiachang Wangeb619222019-07-03 20:52:08 +0800446 List<InetAddress> answer;
Chiachang Wangddb7da62019-06-03 15:50:53 +0800447
Chiachang Wangeb619222019-07-03 20:52:08 +0800448 answer = posType != -1
449 ? getAnswer(invocation.getMock(), hostname, invocation.getArgument(posType)) :
450 queryAllTypes(invocation.getMock(), hostname);
451
Chiachang Wangddb7da62019-06-03 15:50:53 +0800452 if (answer != null && answer.size() > 0) {
453 new Handler(Looper.getMainLooper()).post(() -> {
454 executor.execute(() -> callback.onAnswer(answer, 0));
455 });
456 }
457 // If no answers, do nothing. sendDnsProbeWithTimeout will time out and throw UHE.
458 return null;
459 }
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900460 }
461
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900462 private FakeDns mFakeDns;
463
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900464 @Before
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900465 public void setUp() throws Exception {
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900466 MockitoAnnotations.initMocks(this);
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900467 when(mDependencies.getPrivateDnsBypassNetwork(any())).thenReturn(mCleartextDnsNetwork);
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900468 when(mDependencies.getDnsResolver()).thenReturn(mDnsResolver);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900469 when(mDependencies.getRandom()).thenReturn(mRandom);
470 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt()))
471 .thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
Chiachang Wang79a6da32019-04-17 17:00:54 +0800472 when(mDependencies.getDeviceConfigPropertyInt(any(), eq(CAPTIVE_PORTAL_USE_HTTPS),
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900473 anyInt())).thenReturn(1);
Niklas Lindgren0c904882018-12-07 11:08:04 +0100474 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any()))
475 .thenReturn(TEST_HTTP_URL);
476 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL), any()))
477 .thenReturn(TEST_HTTPS_URL);
478
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900479 doReturn(mCleartextDnsNetwork).when(mNetwork).getPrivateDnsBypassingCopy();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900480
481 when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mCm);
482 when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephony);
483 when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifi);
Niklas Lindgren0c904882018-12-07 11:08:04 +0100484 when(mContext.getResources()).thenReturn(mResources);
485
Remi NGUYEN VAN89cd0262019-12-29 22:46:00 +0900486 when(mServiceManager.getNotifier()).thenReturn(mNotifier);
487
Chiachang Wang9af1cb52020-05-20 15:14:46 +0800488 when(mTelephony.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE);
489 when(mTelephony.getNetworkOperator()).thenReturn(TEST_MCCMNC);
490 when(mTelephony.getSimOperator()).thenReturn(TEST_MCCMNC);
491
Niklas Lindgren0c904882018-12-07 11:08:04 +0100492 when(mResources.getString(anyInt())).thenReturn("");
493 when(mResources.getStringArray(anyInt())).thenReturn(new String[0]);
Chiachang Wang24820992020-03-23 21:16:36 +0800494 doReturn(mConfiguration).when(mResources).getConfiguration();
495 when(mMccContext.getResources()).thenReturn(mMccResource);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900496
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900497 setFallbackUrl(TEST_FALLBACK_URL);
498 setOtherFallbackUrls(TEST_OTHER_FALLBACK_URL);
499 setFallbackSpecs(null); // Test with no fallback spec by default
500 when(mRandom.nextInt()).thenReturn(0);
501
Chiachang Wangcded6ce2019-12-18 17:27:57 +0800502 when(mTstDependencies.getNetd()).thenReturn(mNetd);
Chiachang Wangc11639d2019-05-15 16:18:56 +0800503 // DNS probe timeout should not be defined more than half of HANDLER_TIMEOUT_MS. Otherwise,
504 // it will fail the test because of timeout expired for querying AAAA and A sequentially.
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900505 when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout)))
Chiachang Wangc11639d2019-05-15 16:18:56 +0800506 .thenReturn(200);
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900507
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900508 doAnswer((invocation) -> {
509 URL url = invocation.getArgument(0);
510 switch(url.toString()) {
511 case TEST_HTTP_URL:
512 return mHttpConnection;
Chiachang Wang80be4412020-04-14 11:52:10 +0800513 case TEST_HTTP_OTHER_URL1:
514 return mOtherHttpConnection1;
515 case TEST_HTTP_OTHER_URL2:
516 return mOtherHttpConnection2;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900517 case TEST_HTTPS_URL:
518 return mHttpsConnection;
Chiachang Wang80be4412020-04-14 11:52:10 +0800519 case TEST_HTTPS_OTHER_URL1:
520 return mOtherHttpsConnection1;
521 case TEST_HTTPS_OTHER_URL2:
522 return mOtherHttpsConnection2;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900523 case TEST_FALLBACK_URL:
524 return mFallbackConnection;
525 case TEST_OTHER_FALLBACK_URL:
526 return mOtherFallbackConnection;
Remi NGUYEN VAN943d6342020-03-24 18:22:34 +0900527 case TEST_OVERRIDE_URL:
528 case TEST_INVALID_OVERRIDE_URL:
529 return mTestOverriddenUrlConnection;
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +0900530 case TEST_CAPPORT_API_URL:
531 return mCapportApiConnection;
lucasline8d54032020-04-29 02:35:19 +0800532 case TEST_SPEED_TEST_URL:
533 return mSpeedTestConnection;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900534 default:
535 fail("URL not mocked: " + url.toString());
536 return null;
537 }
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +0900538 }).when(mCleartextDnsNetwork).openConnection(any());
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900539 when(mHttpConnection.getRequestProperties()).thenReturn(new ArrayMap<>());
540 when(mHttpsConnection.getRequestProperties()).thenReturn(new ArrayMap<>());
Lorenzo Colitti171cfd22019-04-18 13:44:32 +0900541
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900542 mFakeDns = new FakeDns();
543 mFakeDns.startMocking();
Chiachang Wangeb619222019-07-03 20:52:08 +0800544 // Set private dns suffix answer. sendPrivateDnsProbe() in NetworkMonitor send probe with
545 // one time hostname. The hostname will be [random generated UUID] + HOST_SUFFIX differently
546 // each time. That means the host answer cannot be pre-set into the answer list. Thus, set
547 // the host suffix and use partial match in FakeDns to match the target host and reply the
548 // intended answer.
549 mFakeDns.setAnswer(PRIVATE_DNS_PROBE_HOST_SUFFIX, new String[]{"192.0.2.2"}, TYPE_A);
550 mFakeDns.setAnswer(PRIVATE_DNS_PROBE_HOST_SUFFIX, new String[]{"2001:db8::1"}, TYPE_AAAA);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900551
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900552 when(mContext.registerReceiver(any(BroadcastReceiver.class), any())).then((invocation) -> {
553 mRegisteredReceivers.add(invocation.getArgument(0));
554 return new Intent();
555 });
556
557 doAnswer((invocation) -> {
558 mRegisteredReceivers.remove(invocation.getArgument(0));
559 return null;
560 }).when(mContext).unregisterReceiver(any());
561
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900562 resetCallbacks();
563
Chiachang Wang4fcbe912021-04-26 13:02:22 +0800564 setMinDataStallEvaluateInterval(TEST_MIN_STALL_EVALUATE_INTERVAL_MS);
Chiachang Wang0e874792019-03-05 20:31:57 +0800565 setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900566 setValidDataStallDnsTimeThreshold(500);
567 setConsecutiveDnsTimeoutThreshold(5);
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900568 mCreatedNetworkMonitors = new HashSet<>();
569 mRegisteredReceivers = new HashSet<>();
Chiachang Wang26a626a2020-03-02 17:41:58 +0800570 setDismissPortalInValidatedNetwork(false);
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900571 }
572
573 @After
574 public void tearDown() {
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900575 mFakeDns.clearAll();
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900576 // Make a local copy of mCreatedNetworkMonitors because during the iteration below,
577 // WrappedNetworkMonitor#onQuitting will delete elements from it on the handler threads.
578 WrappedNetworkMonitor[] networkMonitors = mCreatedNetworkMonitors.toArray(
579 new WrappedNetworkMonitor[0]);
580 for (WrappedNetworkMonitor nm : networkMonitors) {
581 nm.notifyNetworkDisconnected();
582 nm.awaitQuit();
583 }
584 assertEquals("NetworkMonitor still running after disconnect",
585 0, mCreatedNetworkMonitors.size());
586 assertEquals("BroadcastReceiver still registered after disconnect",
587 0, mRegisteredReceivers.size());
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900588 }
589
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900590 private void resetCallbacks() {
Cody Kesting176bce72020-01-20 18:09:59 -0800591 resetCallbacks(6);
592 }
593
594 private void resetCallbacks(int interfaceVersion) {
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900595 reset(mCallbacks);
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900596 try {
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +0900597 doReturn(interfaceVersion).when(mCallbacks).getInterfaceVersion();
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +0900598 } catch (RemoteException e) {
599 // Can't happen as mCallbacks is a mock
600 fail("Error mocking getInterfaceVersion" + e);
601 }
602 }
603
Chiachang Wang9af1cb52020-05-20 15:14:46 +0800604 private TcpSocketTracker getTcpSocketTrackerOrNull(NetworkMonitor.Dependencies dp) {
605 return ((dp.getDeviceConfigPropertyInt(
606 NAMESPACE_CONNECTIVITY,
607 CONFIG_DATA_STALL_EVALUATION_TYPE,
608 DEFAULT_DATA_STALL_EVALUATION_TYPES)
609 & DATA_STALL_EVALUATION_TYPE_TCP) != 0) ? mTst : null;
610 }
611
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900612 private class WrappedNetworkMonitor extends NetworkMonitor {
613 private long mProbeTime = 0;
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900614 private final ConditionVariable mQuitCv = new ConditionVariable(false);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900615
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900616 WrappedNetworkMonitor() {
Remi NGUYEN VAN89cd0262019-12-29 22:46:00 +0900617 super(mContext, mCallbacks, mNetwork, mLogger, mValidationLogger, mServiceManager,
Chiachang Wang9af1cb52020-05-20 15:14:46 +0800618 mDependencies, getTcpSocketTrackerOrNull(mDependencies));
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900619 }
620
621 @Override
622 protected long getLastProbeTime() {
623 return mProbeTime;
624 }
625
626 protected void setLastProbeTime(long time) {
627 mProbeTime = time;
628 }
Chiachang Wangcaa35202019-02-26 11:32:18 +0800629
630 @Override
631 protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) {
Chiachang Wang9af1cb52020-05-20 15:14:46 +0800632 if ((getDataStallEvaluationType() & DATA_STALL_EVALUATION_TYPE_DNS) != 0) {
633 generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
634 }
Chiachang Wangcaa35202019-02-26 11:32:18 +0800635 }
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900636
637 @Override
638 protected void onQuitting() {
639 assertTrue(mCreatedNetworkMonitors.remove(this));
640 mQuitCv.open();
641 }
642
643 protected void awaitQuit() {
644 assertTrue("NetworkMonitor did not quit after " + HANDLER_TIMEOUT_MS + "ms",
645 mQuitCv.block(HANDLER_TIMEOUT_MS));
646 }
lucaslin9b4dfab2019-12-17 23:06:12 +0800647
648 protected Context getContext() {
649 return mContext;
650 }
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900651 }
652
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900653 private WrappedNetworkMonitor makeMonitor(NetworkCapabilities nc) {
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900654 final WrappedNetworkMonitor nm = new WrappedNetworkMonitor();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900655 nm.start();
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +0900656 setNetworkCapabilities(nm, nc);
Chalard Jean23a06302020-06-26 00:41:00 +0900657 HandlerUtils.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS);
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +0900658 mCreatedNetworkMonitors.add(nm);
Chiachang Wanga5716bf2019-11-20 16:13:07 +0800659 when(mTstDependencies.isTcpInfoParsingSupported()).thenReturn(false);
Chiachang Wang4336b912019-11-26 15:39:22 +0800660
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900661 return nm;
662 }
663
Chiachang Wangdaf983b2020-05-07 19:20:52 +0800664 private WrappedNetworkMonitor makeCellMeteredNetworkMonitor() {
665 final WrappedNetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900666 return nm;
667 }
668
Chiachang Wangdaf983b2020-05-07 19:20:52 +0800669 private WrappedNetworkMonitor makeCellNotMeteredNetworkMonitor() {
670 final WrappedNetworkMonitor nm = makeMonitor(CELL_NOT_METERED_CAPABILITIES);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900671 return nm;
672 }
673
Chiachang Wang9af1cb52020-05-20 15:14:46 +0800674 private WrappedNetworkMonitor makeWifiNotMeteredNetworkMonitor() {
675 final WrappedNetworkMonitor nm = makeMonitor(WIFI_NOT_METERED_CAPABILITIES);
676 return nm;
677 }
678
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +0900679 private void setNetworkCapabilities(NetworkMonitor nm, NetworkCapabilities nc) {
680 nm.notifyNetworkCapabilitiesChanged(nc);
Chalard Jean23a06302020-06-26 00:41:00 +0900681 HandlerUtils.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +0900682 }
683
684 @Test
lucasline8d54032020-04-29 02:35:19 +0800685 public void testOnlyWifiTransport() {
Chiachang Wangacb2b702020-05-20 15:41:05 +0800686 final WrappedNetworkMonitor wnm = makeMonitor(CELL_METERED_CAPABILITIES);
687 assertFalse(wnm.onlyWifiTransport());
lucasline8d54032020-04-29 02:35:19 +0800688 final NetworkCapabilities nc = new NetworkCapabilities()
689 .addTransportType(TRANSPORT_WIFI)
690 .addTransportType(TRANSPORT_VPN);
691 setNetworkCapabilities(wnm, nc);
692 assertFalse(wnm.onlyWifiTransport());
693 nc.removeTransportType(TRANSPORT_VPN);
694 setNetworkCapabilities(wnm, nc);
695 assertTrue(wnm.onlyWifiTransport());
696 }
697
698 @Test
699 public void testNeedEvaluatingBandwidth() throws Exception {
700 // Make metered network first, the transport type is TRANSPORT_CELLULAR. That means the
701 // test cannot pass the condition check in needEvaluatingBandwidth().
Chiachang Wangdaf983b2020-05-07 19:20:52 +0800702 final WrappedNetworkMonitor wnm1 = makeCellMeteredNetworkMonitor();
lucasline8d54032020-04-29 02:35:19 +0800703 // Don't set the config_evaluating_bandwidth_url to make
704 // the condition check fail in needEvaluatingBandwidth().
705 assertFalse(wnm1.needEvaluatingBandwidth());
706 // Make the NetworkCapabilities to have the TRANSPORT_WIFI but it still cannot meet the
707 // condition check.
708 final NetworkCapabilities nc = new NetworkCapabilities()
709 .addTransportType(TRANSPORT_WIFI);
710 setNetworkCapabilities(wnm1, nc);
711 assertFalse(wnm1.needEvaluatingBandwidth());
712 // Make the network to be non-metered wifi but it still cannot meet the condition check
713 // since the config_evaluating_bandwidth_url is not set.
714 nc.addCapability(NET_CAPABILITY_NOT_METERED);
715 setNetworkCapabilities(wnm1, nc);
716 assertFalse(wnm1.needEvaluatingBandwidth());
717 // All configurations are set correctly.
718 doReturn(TEST_SPEED_TEST_URL).when(mResources).getString(
719 R.string.config_evaluating_bandwidth_url);
Chiachang Wangdaf983b2020-05-07 19:20:52 +0800720 final WrappedNetworkMonitor wnm2 = makeCellMeteredNetworkMonitor();
lucasline8d54032020-04-29 02:35:19 +0800721 setNetworkCapabilities(wnm2, nc);
722 assertTrue(wnm2.needEvaluatingBandwidth());
723 // Set mIsBandwidthCheckPassedOrIgnored to true and expect needEvaluatingBandwidth() will
724 // return false.
725 wnm2.mIsBandwidthCheckPassedOrIgnored = true;
726 assertFalse(wnm2.needEvaluatingBandwidth());
727 // Reset mIsBandwidthCheckPassedOrIgnored back to false.
728 wnm2.mIsBandwidthCheckPassedOrIgnored = false;
729 // Shouldn't evaluate network bandwidth on the metered wifi.
730 nc.removeCapability(NET_CAPABILITY_NOT_METERED);
731 setNetworkCapabilities(wnm2, nc);
732 assertFalse(wnm2.needEvaluatingBandwidth());
733 // Shouldn't evaluate network bandwidth on the unmetered cellular.
734 nc.addCapability(NET_CAPABILITY_NOT_METERED);
735 nc.removeTransportType(TRANSPORT_WIFI);
736 nc.addTransportType(TRANSPORT_CELLULAR);
737 assertFalse(wnm2.needEvaluatingBandwidth());
738 }
739
740 @Test
741 public void testEvaluatingBandwidthState_meteredNetwork() throws Exception {
742 setStatus(mHttpsConnection, 204);
743 setStatus(mHttpConnection, 204);
744 final NetworkCapabilities meteredCap = new NetworkCapabilities()
745 .addTransportType(TRANSPORT_WIFI)
746 .addCapability(NET_CAPABILITY_INTERNET);
747 doReturn(TEST_SPEED_TEST_URL).when(mResources).getString(
748 R.string.config_evaluating_bandwidth_url);
749 final NetworkMonitor nm = runNetworkTest(TEST_LINK_PROPERTIES, meteredCap,
750 NETWORK_VALIDATION_RESULT_VALID, NETWORK_VALIDATION_PROBE_DNS
751 | NETWORK_VALIDATION_PROBE_HTTPS, null /* redirectUrl */);
752 // Evaluating bandwidth process won't be executed when the network is metered wifi.
753 // Check that the connection hasn't been opened and the state should transition to validated
754 // state directly.
755 verify(mCleartextDnsNetwork, never()).openConnection(new URL(TEST_SPEED_TEST_URL));
756 assertEquals(NETWORK_VALIDATION_RESULT_VALID,
757 nm.getEvaluationState().getEvaluationResult());
758 }
759
760 @Test
761 public void testEvaluatingBandwidthState_nonMeteredNetworkWithWrongConfig() throws Exception {
762 setStatus(mHttpsConnection, 204);
763 setStatus(mHttpConnection, 204);
764 final NetworkCapabilities nonMeteredCap = new NetworkCapabilities()
765 .addTransportType(TRANSPORT_WIFI)
766 .addCapability(NET_CAPABILITY_INTERNET)
767 .addCapability(NET_CAPABILITY_NOT_METERED);
768 doReturn("").when(mResources).getString(R.string.config_evaluating_bandwidth_url);
769 final NetworkMonitor nm = runNetworkTest(TEST_LINK_PROPERTIES, nonMeteredCap,
770 NETWORK_VALIDATION_RESULT_VALID, NETWORK_VALIDATION_PROBE_DNS
771 | NETWORK_VALIDATION_PROBE_HTTPS, null /* redirectUrl */);
772 // Non-metered network with wrong configuration(the config_evaluating_bandwidth_url is
773 // empty). Check that the connection hasn't been opened and the state should transition to
774 // validated state directly.
775 verify(mCleartextDnsNetwork, never()).openConnection(new URL(TEST_SPEED_TEST_URL));
776 assertEquals(NETWORK_VALIDATION_RESULT_VALID,
777 nm.getEvaluationState().getEvaluationResult());
778 }
779
780 @Test
lucaslind4c999f2020-04-09 00:31:21 +0800781 public void testMatchesHttpContent() throws Exception {
Chiachang Wangdaf983b2020-05-07 19:20:52 +0800782 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
lucaslind4c999f2020-04-09 00:31:21 +0800783 doReturn("[\\s\\S]*line2[\\s\\S]*").when(mResources).getString(
784 R.string.config_network_validation_failed_content_regexp);
785 assertTrue(wnm.matchesHttpContent("This is line1\nThis is line2\nThis is line3",
786 R.string.config_network_validation_failed_content_regexp));
787 assertFalse(wnm.matchesHttpContent("hello",
788 R.string.config_network_validation_failed_content_regexp));
789 // Set an invalid regex and expect to get the false even though the regex is the same as the
790 // content.
791 doReturn("[").when(mResources).getString(
792 R.string.config_network_validation_failed_content_regexp);
793 assertFalse(wnm.matchesHttpContent("[",
794 R.string.config_network_validation_failed_content_regexp));
795 }
796
797 @Test
798 public void testMatchesHttpContentLength() throws Exception {
Chiachang Wangdaf983b2020-05-07 19:20:52 +0800799 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
lucaslind4c999f2020-04-09 00:31:21 +0800800 // Set the range of content length.
801 doReturn(100).when(mResources).getInteger(R.integer.config_min_matches_http_content_length);
802 doReturn(1000).when(mResources).getInteger(
803 R.integer.config_max_matches_http_content_length);
804 assertFalse(wnm.matchesHttpContentLength(100));
805 assertFalse(wnm.matchesHttpContentLength(1000));
806 assertTrue(wnm.matchesHttpContentLength(500));
807
808 // Test the invalid value.
809 assertFalse(wnm.matchesHttpContentLength(-1));
810 assertFalse(wnm.matchesHttpContentLength(0));
811 assertFalse(wnm.matchesHttpContentLength(Integer.MAX_VALUE + 1L));
812
813 // Set the wrong value for min and max config to make sure the function is working even
814 // though the config is wrong.
815 doReturn(1000).when(mResources).getInteger(
816 R.integer.config_min_matches_http_content_length);
817 doReturn(100).when(mResources).getInteger(
818 R.integer.config_max_matches_http_content_length);
819 assertFalse(wnm.matchesHttpContentLength(100));
820 assertFalse(wnm.matchesHttpContentLength(1000));
821 assertFalse(wnm.matchesHttpContentLength(500));
822 }
823
824 @Test
825 public void testGetResStringConfig() throws Exception {
Chiachang Wangdaf983b2020-05-07 19:20:52 +0800826 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
lucaslind4c999f2020-04-09 00:31:21 +0800827 // Set the config and expect to get the customized value.
828 final String regExp = ".*HTTP.*200.*not a captive portal.*";
829 doReturn(regExp).when(mResources).getString(
830 R.string.config_network_validation_failed_content_regexp);
831 assertEquals(regExp, wnm.getResStringConfig(mContext,
832 R.string.config_network_validation_failed_content_regexp, null));
833 doThrow(new Resources.NotFoundException()).when(mResources).getString(eq(
834 R.string.config_network_validation_failed_content_regexp));
835 // If the config is not found, then expect to get the default value - null.
836 assertNull(wnm.getResStringConfig(mContext,
837 R.string.config_network_validation_failed_content_regexp, null));
838 }
839
840 @Test
841 public void testGetResIntConfig() throws Exception {
Chiachang Wangdaf983b2020-05-07 19:20:52 +0800842 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
lucaslind4c999f2020-04-09 00:31:21 +0800843 // Set the config and expect to get the customized value.
844 doReturn(100).when(mResources).getInteger(R.integer.config_min_matches_http_content_length);
845 doReturn(1000).when(mResources).getInteger(
846 R.integer.config_max_matches_http_content_length);
847 assertEquals(100, wnm.getResIntConfig(mContext,
848 R.integer.config_min_matches_http_content_length, Integer.MAX_VALUE));
849 assertEquals(1000, wnm.getResIntConfig(mContext,
850 R.integer.config_max_matches_http_content_length, 0));
851 doThrow(new Resources.NotFoundException())
852 .when(mResources).getInteger(
853 eq(R.integer.config_min_matches_http_content_length));
854 doThrow(new Resources.NotFoundException())
855 .when(mResources).getInteger(eq(R.integer.config_max_matches_http_content_length));
856 // If the config is not found, then expect to get the default value.
857 assertEquals(Integer.MAX_VALUE, wnm.getResIntConfig(mContext,
858 R.integer.config_min_matches_http_content_length, Integer.MAX_VALUE));
859 assertEquals(0, wnm.getResIntConfig(mContext,
860 R.integer.config_max_matches_http_content_length, 0));
861 }
862
863 @Test
Chiachang Wang5dbe6852020-04-10 09:02:45 +0800864 public void testGetHttpProbeUrl() {
Chiachang Wangdaf983b2020-05-07 19:20:52 +0800865 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
Chiachang Wang5dbe6852020-04-10 09:02:45 +0800866 // If config_captive_portal_http_url is set and the global setting is set, the config is
867 // used.
868 doReturn(TEST_HTTP_URL).when(mResources).getString(R.string.config_captive_portal_http_url);
869 doReturn(TEST_HTTP_OTHER_URL2).when(mResources).getString(
870 R.string.default_captive_portal_http_url);
871 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any()))
872 .thenReturn(TEST_HTTP_OTHER_URL1);
873 assertEquals(TEST_HTTP_URL, wnm.getCaptivePortalServerHttpUrl());
874 // If config_captive_portal_http_url is unset and the global setting is set, the global
875 // setting is used.
876 doReturn(null).when(mResources).getString(R.string.config_captive_portal_http_url);
877 assertEquals(TEST_HTTP_OTHER_URL1, wnm.getCaptivePortalServerHttpUrl());
878 // If both config_captive_portal_http_url and global setting are unset,
879 // default_captive_portal_http_url is used.
880 when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any()))
881 .thenReturn(null);
882 assertEquals(TEST_HTTP_OTHER_URL2, wnm.getCaptivePortalServerHttpUrl());
883 }
884
885 @Test
lucaslin9b4dfab2019-12-17 23:06:12 +0800886 public void testGetLocationMcc() throws Exception {
Chiachang Wangdaf983b2020-05-07 19:20:52 +0800887 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
lucaslin9b4dfab2019-12-17 23:06:12 +0800888 doReturn(PackageManager.PERMISSION_DENIED).when(mContext).checkPermission(
889 eq(android.Manifest.permission.ACCESS_FINE_LOCATION), anyInt(), anyInt());
890 assertNull(wnm.getLocationMcc());
891 doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkPermission(
892 eq(android.Manifest.permission.ACCESS_FINE_LOCATION), anyInt(), anyInt());
893 doReturn(new ContextWrapper(mContext)).when(mContext).createConfigurationContext(any());
lucaslinc4b59dd2020-06-02 16:31:21 +0800894 doReturn(null).when(mTelephony).getAllCellInfo();
895 assertNull(wnm.getLocationMcc());
lucaslin9b4dfab2019-12-17 23:06:12 +0800896 // Prepare CellInfo and check if the vote mechanism is working or not.
lucaslin9b4dfab2019-12-17 23:06:12 +0800897 final List<CellInfo> cellList = new ArrayList<CellInfo>();
lucaslinc4b59dd2020-06-02 16:31:21 +0800898 doReturn(cellList).when(mTelephony).getAllCellInfo();
899 assertNull(wnm.getLocationMcc());
Chiachang Wang24820992020-03-23 21:16:36 +0800900 cellList.add(makeTestCellInfoGsm("460"));
901 cellList.add(makeTestCellInfoGsm("460"));
902 cellList.add(makeTestCellInfoLte("466"));
lucaslin9b4dfab2019-12-17 23:06:12 +0800903 // The count of 460 is 2 and the count of 466 is 1, so the getLocationMcc() should return
904 // 460.
905 assertEquals("460", wnm.getLocationMcc());
lucaslin9073e7b2020-04-15 23:02:14 +0800906 // getCustomizedContextOrDefault() shouldn't return mContext when using neighbor mcc
lucaslin9b4dfab2019-12-17 23:06:12 +0800907 // is enabled and the sim is not ready.
908 doReturn(true).when(mResources).getBoolean(R.bool.config_no_sim_card_uses_neighbor_mcc);
909 doReturn(TelephonyManager.SIM_STATE_ABSENT).when(mTelephony).getSimState();
lucaslin9b4dfab2019-12-17 23:06:12 +0800910 assertEquals(460,
lucaslin9073e7b2020-04-15 23:02:14 +0800911 wnm.getCustomizedContextOrDefault().getResources().getConfiguration().mcc);
lucaslin9b4dfab2019-12-17 23:06:12 +0800912 doReturn(false).when(mResources).getBoolean(R.bool.config_no_sim_card_uses_neighbor_mcc);
lucaslin9073e7b2020-04-15 23:02:14 +0800913 assertEquals(wnm.getContext(), wnm.getCustomizedContextOrDefault());
914 }
915
916 @Test
917 public void testGetMccMncOverrideInfo() {
Chiachang Wangdaf983b2020-05-07 19:20:52 +0800918 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
lucaslin9073e7b2020-04-15 23:02:14 +0800919 doReturn(new ContextWrapper(mContext)).when(mContext).createConfigurationContext(any());
920 // 1839 is VZW's carrier id.
921 doReturn(1839).when(mTelephony).getSimCarrierId();
922 assertNull(wnm.getMccMncOverrideInfo());
923 // 1854 is CTC's carrier id.
924 doReturn(1854).when(mTelephony).getSimCarrierId();
925 assertNotNull(wnm.getMccMncOverrideInfo());
926 // Check if the mcc & mnc has changed as expected.
927 assertEquals(460,
928 wnm.getCustomizedContextOrDefault().getResources().getConfiguration().mcc);
929 assertEquals(03,
930 wnm.getCustomizedContextOrDefault().getResources().getConfiguration().mnc);
931 // Every mcc and mnc should be set in sCarrierIdToMccMnc.
932 // Check if there is any unset value in mcc or mnc.
933 for (int i = 0; i < wnm.sCarrierIdToMccMnc.size(); i++) {
934 assertNotEquals(-1, wnm.sCarrierIdToMccMnc.valueAt(i).mcc);
935 assertNotEquals(-1, wnm.sCarrierIdToMccMnc.valueAt(i).mnc);
936 }
lucaslin9b4dfab2019-12-17 23:06:12 +0800937 }
938
Chiachang Wang24820992020-03-23 21:16:36 +0800939 private CellInfoGsm makeTestCellInfoGsm(String mcc) throws Exception {
940 final CellInfoGsm info = new CellInfoGsm();
941 final CellIdentityGsm ci = makeCellIdentityGsm(0, 0, 0, 0, mcc, "01", "", "");
942 info.setCellIdentity(ci);
943 return info;
944 }
945
946 private CellInfoLte makeTestCellInfoLte(String mcc) throws Exception {
947 final CellInfoLte info = new CellInfoLte();
948 final CellIdentityLte ci = makeCellIdentityLte(0, 0, 0, 0, 0, mcc, "01", "", "");
949 info.setCellIdentity(ci);
950 return info;
951 }
952
lucaslinb9826c52020-06-19 14:47:01 +0800953 private void setupNoSimCardNeighborMcc() throws Exception {
954 // Enable using neighbor resource by camping mcc feature.
955 doReturn(true).when(mResources).getBoolean(R.bool.config_no_sim_card_uses_neighbor_mcc);
956 final List<CellInfo> cellList = new ArrayList<CellInfo>();
957 final int testMcc = 460;
958 cellList.add(makeTestCellInfoGsm(Integer.toString(testMcc)));
959 doReturn(cellList).when(mTelephony).getAllCellInfo();
960 final Configuration config = mResources.getConfiguration();
961 config.mcc = testMcc;
962 doReturn(mMccContext).when(mContext).createConfigurationContext(eq(config));
963 }
964
Chiachang Wang24820992020-03-23 21:16:36 +0800965 @Test
966 public void testMakeFallbackUrls() throws Exception {
Chiachang Wangdaf983b2020-05-07 19:20:52 +0800967 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
Chiachang Wang24820992020-03-23 21:16:36 +0800968 // Value exist in setting provider.
969 URL[] urls = wnm.makeCaptivePortalFallbackUrls();
970 assertEquals(urls[0].toString(), TEST_FALLBACK_URL);
971
972 // Clear setting provider value. Verify it to get configuration from resource instead.
973 setFallbackUrl(null);
974 // Verify that getting resource with exception.
975 when(mResources.getStringArray(R.array.config_captive_portal_fallback_urls))
976 .thenThrow(Resources.NotFoundException.class);
977 urls = wnm.makeCaptivePortalFallbackUrls();
978 assertEquals(urls.length, 0);
979
980 // Verify resource return 2 different URLs.
981 doReturn(new String[] {"http://testUrl1.com", "http://testUrl2.com"}).when(mResources)
982 .getStringArray(R.array.config_captive_portal_fallback_urls);
983 urls = wnm.makeCaptivePortalFallbackUrls();
984 assertEquals(urls.length, 2);
985 assertEquals("http://testUrl1.com", urls[0].toString());
986 assertEquals("http://testUrl2.com", urls[1].toString());
987
lucaslinb9826c52020-06-19 14:47:01 +0800988 // Even though the using neighbor resource by camping mcc feature is enabled, the
989 // customized context has been assigned and won't change. So calling
990 // makeCaptivePortalFallbackUrls() still gets the original value.
991 setupNoSimCardNeighborMcc();
Chiachang Wang24820992020-03-23 21:16:36 +0800992 doReturn(new String[] {"http://testUrl3.com"}).when(mMccResource)
993 .getStringArray(R.array.config_captive_portal_fallback_urls);
994 urls = wnm.makeCaptivePortalFallbackUrls();
lucaslinb9826c52020-06-19 14:47:01 +0800995 assertEquals(urls.length, 2);
996 assertEquals("http://testUrl1.com", urls[0].toString());
997 assertEquals("http://testUrl2.com", urls[1].toString());
998 }
999
1000 @Test
1001 public void testMakeFallbackUrlsWithCustomizedContext() throws Exception {
1002 // Value is expected to be replaced by location resource.
1003 setupNoSimCardNeighborMcc();
1004 doReturn(new String[] {"http://testUrl.com"}).when(mMccResource)
1005 .getStringArray(R.array.config_captive_portal_fallback_urls);
1006 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
1007 final URL[] urls = wnm.makeCaptivePortalFallbackUrls();
Chiachang Wang24820992020-03-23 21:16:36 +08001008 assertEquals(urls.length, 1);
lucaslinb9826c52020-06-19 14:47:01 +08001009 assertEquals("http://testUrl.com", urls[0].toString());
Chiachang Wang24820992020-03-23 21:16:36 +08001010 }
1011
Remi NGUYEN VAN7fc260a2020-02-03 13:33:58 +09001012 private static CellIdentityGsm makeCellIdentityGsm(int lac, int cid, int arfcn, int bsic,
1013 String mccStr, String mncStr, String alphal, String alphas)
1014 throws ReflectiveOperationException {
Lorenzo Colitti0d08b442020-04-27 11:49:39 +09001015 if (ShimUtils.isAtLeastR()) {
Remi NGUYEN VAN7fc260a2020-02-03 13:33:58 +09001016 return new CellIdentityGsm(lac, cid, arfcn, bsic, mccStr, mncStr, alphal, alphas,
1017 Collections.emptyList() /* additionalPlmns */);
1018 } else {
1019 // API <= Q does not have the additionalPlmns parameter
1020 final Constructor<CellIdentityGsm> constructor = CellIdentityGsm.class.getConstructor(
1021 int.class, int.class, int.class, int.class, String.class, String.class,
1022 String.class, String.class);
1023 return constructor.newInstance(lac, cid, arfcn, bsic, mccStr, mncStr, alphal, alphas);
1024 }
1025 }
1026
1027 private static CellIdentityLte makeCellIdentityLte(int ci, int pci, int tac, int earfcn,
1028 int bandwidth, String mccStr, String mncStr, String alphal, String alphas)
1029 throws ReflectiveOperationException {
Lorenzo Colitti0d08b442020-04-27 11:49:39 +09001030 if (ShimUtils.isAtLeastR()) {
Nathan Haroldbadf7882020-03-03 14:40:26 -08001031 return new CellIdentityLte(ci, pci, tac, earfcn, new int[] {} /* bands */,
Jack Yucfedb152020-02-22 23:05:08 -08001032 bandwidth, mccStr, mncStr, alphal, alphas,
1033 Collections.emptyList() /* additionalPlmns */, null /* csgInfo */);
Remi NGUYEN VAN7fc260a2020-02-03 13:33:58 +09001034 } else {
1035 // API <= Q does not have the additionalPlmns and csgInfo parameters
1036 final Constructor<CellIdentityLte> constructor = CellIdentityLte.class.getConstructor(
1037 int.class, int.class, int.class, int.class, int.class, String.class,
1038 String.class, String.class, String.class);
1039 return constructor.newInstance(ci, pci, tac, earfcn, bandwidth, mccStr, mncStr, alphal,
1040 alphas);
1041 }
1042 }
1043
lucaslin9b4dfab2019-12-17 23:06:12 +08001044 @Test
Lorenzo Colitti171cfd22019-04-18 13:44:32 +09001045 public void testGetIntSetting() throws Exception {
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001046 WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
Lorenzo Colitti171cfd22019-04-18 13:44:32 +09001047
1048 // No config resource, no device config. Expect to get default resource.
1049 doThrow(new Resources.NotFoundException())
1050 .when(mResources).getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout));
1051 doAnswer(invocation -> {
1052 int defaultValue = invocation.getArgument(2);
1053 return defaultValue;
1054 }).when(mDependencies).getDeviceConfigPropertyInt(any(),
1055 eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT),
1056 anyInt());
Chiachang Wang5dbe6852020-04-10 09:02:45 +08001057 assertEquals(DEFAULT_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, wnm.getIntSetting(mContext,
Lorenzo Colitti171cfd22019-04-18 13:44:32 +09001058 R.integer.config_captive_portal_dns_probe_timeout,
1059 NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT,
Chiachang Wang5dbe6852020-04-10 09:02:45 +08001060 DEFAULT_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT));
Lorenzo Colitti171cfd22019-04-18 13:44:32 +09001061
1062 // Set device config. Expect to get device config.
1063 when(mDependencies.getDeviceConfigPropertyInt(any(),
1064 eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT), anyInt()))
1065 .thenReturn(1234);
1066 assertEquals(1234, wnm.getIntSetting(mContext,
1067 R.integer.config_captive_portal_dns_probe_timeout,
1068 NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT,
Chiachang Wang5dbe6852020-04-10 09:02:45 +08001069 DEFAULT_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT));
Lorenzo Colitti171cfd22019-04-18 13:44:32 +09001070
1071 // Set config resource. Expect to get config resource.
1072 when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout)))
1073 .thenReturn(5678);
1074 assertEquals(5678, wnm.getIntSetting(mContext,
1075 R.integer.config_captive_portal_dns_probe_timeout,
1076 NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT,
Chiachang Wang5dbe6852020-04-10 09:02:45 +08001077 DEFAULT_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT));
Lorenzo Colitti171cfd22019-04-18 13:44:32 +09001078 }
1079
1080 @Test
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001081 public void testIsCaptivePortal_HttpProbeIsPortal() throws Exception {
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001082 setSslException(mHttpsConnection);
1083 setPortal302(mHttpConnection);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001084 runPortalNetworkTest();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001085 }
1086
Remi NGUYEN VANa7500732021-05-27 14:35:18 +09001087 @Test
1088 public void testIsCaptivePortal_Http200EmptyResponse() throws Exception {
1089 setSslException(mHttpsConnection);
1090 setStatus(mHttpConnection, 200);
1091 // Invalid if there is no content (can't login to an empty page)
1092 runNetworkTest(VALIDATION_RESULT_INVALID, 0 /* probesSucceeded */, null);
1093 verify(mCallbacks, never()).showProvisioningNotification(any(), any());
1094 }
1095
1096 private void doCaptivePortal200ResponseTest(String expectedRedirectUrl) throws Exception {
1097 setSslException(mHttpsConnection);
1098 setStatus(mHttpConnection, 200);
1099 doReturn(100L).when(mHttpConnection).getContentLengthLong();
1100 // Redirect URL was null before S
1101 runNetworkTest(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, expectedRedirectUrl);
1102 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)).showProvisioningNotification(any(), any());
1103 }
1104
1105 @Test @IgnoreAfter(Build.VERSION_CODES.R)
1106 public void testIsCaptivePortal_HttpProbeIs200Portal_R() throws Exception {
1107 doCaptivePortal200ResponseTest(null);
1108 }
1109
1110 @Test @IgnoreUpTo(Build.VERSION_CODES.R)
1111 public void testIsCaptivePortal_HttpProbeIs200Portal() throws Exception {
1112 doCaptivePortal200ResponseTest(TEST_HTTP_URL);
1113 }
1114
Yeo4915faf2019-08-26 14:53:23 +09001115 private void setupPrivateIpResponse(String privateAddr) throws Exception {
1116 setSslException(mHttpsConnection);
1117 setPortal302(mHttpConnection);
1118 final String httpHost = new URL(TEST_HTTP_URL).getHost();
1119 mFakeDns.setAnswer(httpHost, new String[] { "2001:db8::123" }, TYPE_AAAA);
1120 final InetAddress parsedPrivateAddr = InetAddresses.parseNumericAddress(privateAddr);
1121 mFakeDns.setAnswer(httpHost, new String[] { privateAddr },
1122 (parsedPrivateAddr instanceof Inet6Address) ? TYPE_AAAA : TYPE_A);
1123 }
1124
1125 @Test
1126 public void testIsCaptivePortal_PrivateIpNotPortal_Enabled_IPv4() throws Exception {
1127 when(mDependencies.isFeatureEnabled(any(), eq(DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION)))
1128 .thenReturn(true);
1129 setupPrivateIpResponse("192.168.1.1");
1130 runFailedNetworkTest();
1131 }
1132
1133 @Test
1134 public void testIsCaptivePortal_PrivateIpNotPortal_Enabled_IPv6() throws Exception {
1135 when(mDependencies.isFeatureEnabled(any(), eq(DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION)))
1136 .thenReturn(true);
1137 setupPrivateIpResponse("fec0:1234::1");
1138 runFailedNetworkTest();
1139 }
1140
1141 @Test
1142 public void testIsCaptivePortal_PrivateIpNotPortal_Disabled() throws Exception {
1143 setupPrivateIpResponse("192.168.1.1");
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001144 runPortalNetworkTest();
Yeo4915faf2019-08-26 14:53:23 +09001145 }
1146
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001147 @Test
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001148 public void testIsCaptivePortal_HttpsProbeIsNotPortal() throws Exception {
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001149 setStatus(mHttpsConnection, 204);
1150 setStatus(mHttpConnection, 500);
1151
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001152 runValidatedNetworkTest();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001153 }
1154
1155 @Test
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001156 public void testIsCaptivePortal_FallbackProbeIsPortal() throws Exception {
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001157 setSslException(mHttpsConnection);
1158 setStatus(mHttpConnection, 500);
1159 setPortal302(mFallbackConnection);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001160 runPortalNetworkTest();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001161 }
1162
1163 @Test
Chiachang Wangc9a89aa2020-11-13 17:04:35 +08001164 public void testIsCaptivePortal_HttpSucceedFallbackProbeIsPortal() throws Exception {
1165 setSslException(mHttpsConnection);
1166 setStatus(mHttpConnection, 204);
1167 setPortal302(mFallbackConnection);
1168 runPortalNetworkTest();
1169 }
1170
1171 @Test
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001172 public void testIsCaptivePortal_FallbackProbeIsNotPortal() throws Exception {
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001173 setSslException(mHttpsConnection);
1174 setStatus(mHttpConnection, 500);
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09001175 setStatus(mFallbackConnection, 500);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001176
1177 // Fallback probe did not see portal, HTTPS failed -> inconclusive
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09001178 runFailedNetworkTest();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001179 }
1180
1181 @Test
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001182 public void testIsCaptivePortal_OtherFallbackProbeIsPortal() throws Exception {
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001183 // Set all fallback probes but one to invalid URLs to verify they are being skipped
1184 setFallbackUrl(TEST_FALLBACK_URL);
1185 setOtherFallbackUrls(TEST_FALLBACK_URL + "," + TEST_OTHER_FALLBACK_URL);
1186
1187 setSslException(mHttpsConnection);
1188 setStatus(mHttpConnection, 500);
1189 setStatus(mFallbackConnection, 500);
1190 setPortal302(mOtherFallbackConnection);
1191
1192 // TEST_OTHER_FALLBACK_URL is third
1193 when(mRandom.nextInt()).thenReturn(2);
1194
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001195 // First check always uses the first fallback URL: inconclusive
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001196 final NetworkMonitor monitor = runFailedNetworkTest();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001197 verify(mFallbackConnection, times(1)).getResponseCode();
1198 verify(mOtherFallbackConnection, never()).getResponseCode();
1199
Remi NGUYEN VAN2f187302020-05-21 14:35:50 +09001200 // Second check should be triggered automatically after the reevaluate delay, and uses the
1201 // URL chosen by mRandom
Remi NGUYEN VAN396931d2020-05-28 17:04:21 +09001202 // Ensure that the reevaluate delay is not changed to a large value, otherwise this test
1203 // would block for too long and a different test strategy should be used.
Remi NGUYEN VAN2f187302020-05-21 14:35:50 +09001204 assertTrue(INITIAL_REEVALUATE_DELAY_MS < 2000);
1205 verify(mOtherFallbackConnection, timeout(INITIAL_REEVALUATE_DELAY_MS + HANDLER_TIMEOUT_MS))
1206 .getResponseCode();
1207 verifyNetworkTested(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, TEST_LOGIN_URL);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001208 }
1209
1210 @Test
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001211 public void testIsCaptivePortal_AllProbesFailed() throws Exception {
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001212 setSslException(mHttpsConnection);
1213 setStatus(mHttpConnection, 500);
1214 setStatus(mFallbackConnection, 404);
1215
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09001216 runFailedNetworkTest();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001217 verify(mFallbackConnection, times(1)).getResponseCode();
1218 verify(mOtherFallbackConnection, never()).getResponseCode();
1219 }
1220
1221 @Test
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001222 public void testIsCaptivePortal_InvalidUrlSkipped() throws Exception {
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001223 setFallbackUrl("invalid");
1224 setOtherFallbackUrls("otherinvalid," + TEST_OTHER_FALLBACK_URL + ",yetanotherinvalid");
1225
1226 setSslException(mHttpsConnection);
1227 setStatus(mHttpConnection, 500);
1228 setPortal302(mOtherFallbackConnection);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001229 runPortalNetworkTest();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001230 verify(mOtherFallbackConnection, times(1)).getResponseCode();
1231 verify(mFallbackConnection, never()).getResponseCode();
1232 }
1233
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001234 @Test
Chiachang Wangaa550582020-05-27 17:18:20 +08001235 public void testIsCaptivePortal_CapportApiIsPortalWithNullPortalUrl() throws Exception {
1236 assumeTrue(CaptivePortalDataShimImpl.isSupported());
1237 setSslException(mHttpsConnection);
1238 final long bytesRemaining = 10_000L;
1239 final long secondsRemaining = 500L;
1240 // Set content without partal url.
1241 setApiContent(mCapportApiConnection, "{'captive': true,"
1242 + "'venue-info-url': '" + TEST_VENUE_INFO_URL + "',"
1243 + "'bytes-remaining': " + bytesRemaining + ","
1244 + "'seconds-remaining': " + secondsRemaining + "}");
1245 setPortal302(mHttpConnection);
1246
1247 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES, VALIDATION_RESULT_PORTAL,
1248 0 /* probesSucceeded*/, TEST_LOGIN_URL);
1249
1250 verify(mCapportApiConnection).getResponseCode();
1251
1252 verify(mHttpConnection, times(1)).getResponseCode();
1253 verify(mCallbacks, never()).notifyCaptivePortalDataChanged(any());
1254 }
1255
1256 @Test
1257 public void testIsCaptivePortal_CapportApiIsPortalWithValidPortalUrl() throws Exception {
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001258 assumeTrue(CaptivePortalDataShimImpl.isSupported());
1259 setSslException(mHttpsConnection);
1260 final long bytesRemaining = 10_000L;
1261 final long secondsRemaining = 500L;
1262
1263 setApiContent(mCapportApiConnection, "{'captive': true,"
1264 + "'user-portal-url': '" + TEST_LOGIN_URL + "',"
1265 + "'venue-info-url': '" + TEST_VENUE_INFO_URL + "',"
1266 + "'bytes-remaining': " + bytesRemaining + ","
1267 + "'seconds-remaining': " + secondsRemaining + "}");
1268
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001269 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES, VALIDATION_RESULT_PORTAL,
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001270 0 /* probesSucceeded*/, TEST_LOGIN_URL);
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001271
1272 verify(mHttpConnection, never()).getResponseCode();
1273 verify(mCapportApiConnection).getResponseCode();
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001274
1275 final ArgumentCaptor<CaptivePortalData> capportDataCaptor =
1276 ArgumentCaptor.forClass(CaptivePortalData.class);
1277 verify(mCallbacks).notifyCaptivePortalDataChanged(capportDataCaptor.capture());
1278 final CaptivePortalData p = capportDataCaptor.getValue();
1279 assertTrue(p.isCaptive());
1280 assertEquals(Uri.parse(TEST_LOGIN_URL), p.getUserPortalUrl());
1281 assertEquals(Uri.parse(TEST_VENUE_INFO_URL), p.getVenueInfoUrl());
1282 assertEquals(bytesRemaining, p.getByteLimit());
1283 final long expectedExpiry = currentTimeMillis() + secondsRemaining * 1000;
1284 // Actual expiry will be slightly lower as some time as passed
1285 assertTrue(p.getExpiryTimeMillis() <= expectedExpiry);
1286 assertTrue(p.getExpiryTimeMillis() > expectedExpiry - 30_000);
1287 }
1288
1289 @Test
1290 public void testIsCaptivePortal_CapportApiRevalidation() throws Exception {
1291 assumeTrue(CaptivePortalDataShimImpl.isSupported());
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001292 setValidProbes();
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001293 final NetworkMonitor nm = runValidatedNetworkTest();
1294
1295 setApiContent(mCapportApiConnection, "{'captive': true, "
1296 + "'user-portal-url': '" + TEST_LOGIN_URL + "'}");
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +09001297 nm.notifyLinkPropertiesChanged(makeCapportLPs());
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001298
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001299 verifyNetworkTested(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */,
1300 TEST_LOGIN_URL);
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +09001301 final ArgumentCaptor<CaptivePortalData> capportCaptor = ArgumentCaptor.forClass(
1302 CaptivePortalData.class);
1303 verify(mCallbacks).notifyCaptivePortalDataChanged(capportCaptor.capture());
1304 assertEquals(Uri.parse(TEST_LOGIN_URL), capportCaptor.getValue().getUserPortalUrl());
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001305
1306 // HTTP probe was sent on first validation but not re-sent when there was a portal URL.
1307 verify(mHttpConnection, times(1)).getResponseCode();
1308 verify(mCapportApiConnection, times(1)).getResponseCode();
1309 }
1310
1311 @Test
Remi NGUYEN VANebb5a9f2020-05-20 19:11:56 +09001312 public void testIsCaptivePortal_NoRevalidationBeforeNetworkConnected() throws Exception {
1313 assumeTrue(CaptivePortalDataShimImpl.isSupported());
1314
1315 final NetworkMonitor nm = makeCellMeteredNetworkMonitor();
1316
1317 final LinkProperties lp = makeCapportLPs();
1318
1319 // LinkProperties changed, but NM should not revalidate before notifyNetworkConnected
1320 nm.notifyLinkPropertiesChanged(lp);
1321 verify(mHttpConnection, after(100).never()).getResponseCode();
1322 verify(mHttpsConnection, never()).getResponseCode();
1323 verify(mCapportApiConnection, never()).getResponseCode();
1324
1325 setValidProbes();
1326 setApiContent(mCapportApiConnection, "{'captive': true, "
1327 + "'user-portal-url': '" + TEST_LOGIN_URL + "'}");
1328
1329 // After notifyNetworkConnected, validation uses the capport API contents
1330 nm.notifyNetworkConnected(lp, CELL_METERED_CAPABILITIES);
1331 verifyNetworkTested(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, TEST_LOGIN_URL);
1332
1333 verify(mHttpConnection, never()).getResponseCode();
1334 verify(mCapportApiConnection).getResponseCode();
1335 }
1336
1337 @Test
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001338 public void testIsCaptivePortal_CapportApiNotPortalNotValidated() throws Exception {
1339 assumeTrue(CaptivePortalDataShimImpl.isSupported());
1340 setSslException(mHttpsConnection);
1341 setStatus(mHttpConnection, 500);
1342 setApiContent(mCapportApiConnection, "{'captive': false,"
1343 + "'venue-info-url': '" + TEST_VENUE_INFO_URL + "'}");
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001344 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES, VALIDATION_RESULT_INVALID,
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001345 0 /* probesSucceeded */, null /* redirectUrl */);
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001346
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +09001347 final ArgumentCaptor<CaptivePortalData> capportCaptor = ArgumentCaptor.forClass(
1348 CaptivePortalData.class);
1349 verify(mCallbacks).notifyCaptivePortalDataChanged(capportCaptor.capture());
1350 assertEquals(Uri.parse(TEST_VENUE_INFO_URL), capportCaptor.getValue().getVenueInfoUrl());
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001351 }
1352
1353 @Test
1354 public void testIsCaptivePortal_CapportApiNotPortalPartial() throws Exception {
1355 assumeTrue(CaptivePortalDataShimImpl.isSupported());
1356 setSslException(mHttpsConnection);
1357 setStatus(mHttpConnection, 204);
1358 setApiContent(mCapportApiConnection, "{'captive': false,"
1359 + "'venue-info-url': '" + TEST_VENUE_INFO_URL + "'}");
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001360 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES,
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001361 NETWORK_VALIDATION_RESULT_PARTIAL,
1362 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP,
1363 null /* redirectUrl */);
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001364
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +09001365 final ArgumentCaptor<CaptivePortalData> capportCaptor = ArgumentCaptor.forClass(
1366 CaptivePortalData.class);
1367 verify(mCallbacks).notifyCaptivePortalDataChanged(capportCaptor.capture());
1368 assertEquals(Uri.parse(TEST_VENUE_INFO_URL), capportCaptor.getValue().getVenueInfoUrl());
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001369 }
1370
1371 @Test
1372 public void testIsCaptivePortal_CapportApiNotPortalValidated() throws Exception {
1373 assumeTrue(CaptivePortalDataShimImpl.isSupported());
1374 setStatus(mHttpsConnection, 204);
1375 setStatus(mHttpConnection, 204);
1376 setApiContent(mCapportApiConnection, "{'captive': false,"
1377 + "'venue-info-url': '" + TEST_VENUE_INFO_URL + "'}");
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001378 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES,
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001379 NETWORK_VALIDATION_RESULT_VALID,
1380 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP
1381 | NETWORK_VALIDATION_PROBE_HTTPS,
1382 null /* redirectUrl */);
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001383
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +09001384 final ArgumentCaptor<CaptivePortalData> capportCaptor = ArgumentCaptor.forClass(
1385 CaptivePortalData.class);
1386 verify(mCallbacks).notifyCaptivePortalDataChanged(capportCaptor.capture());
1387 assertEquals(Uri.parse(TEST_VENUE_INFO_URL), capportCaptor.getValue().getVenueInfoUrl());
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001388 }
1389
1390 @Test
1391 public void testIsCaptivePortal_CapportApiInvalidContent() throws Exception {
1392 assumeTrue(CaptivePortalDataShimImpl.isSupported());
Remi NGUYEN VANb6949332020-05-12 18:03:50 +09001393 setSslException(mHttpsConnection);
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001394 setPortal302(mHttpConnection);
1395 setApiContent(mCapportApiConnection, "{SomeInvalidText");
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001396 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES,
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001397 VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */,
1398 TEST_LOGIN_URL);
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001399
1400 verify(mCallbacks, never()).notifyCaptivePortalDataChanged(any());
1401 verify(mHttpConnection).getResponseCode();
1402 }
1403
Remi NGUYEN VANb6949332020-05-12 18:03:50 +09001404 private void runCapportApiInvalidUrlTest(String url) throws Exception {
1405 assumeTrue(CaptivePortalDataShimImpl.isSupported());
1406 setSslException(mHttpsConnection);
1407 setPortal302(mHttpConnection);
1408 final LinkProperties lp = new LinkProperties(TEST_LINK_PROPERTIES);
1409 lp.setCaptivePortalApiUrl(Uri.parse(url));
1410 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES,
1411 VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */,
1412 TEST_LOGIN_URL);
1413
1414 verify(mCallbacks, never()).notifyCaptivePortalDataChanged(any());
1415 verify(mCapportApiConnection, never()).getInputStream();
1416 verify(mHttpConnection).getResponseCode();
1417 }
1418
1419 @Test
1420 public void testIsCaptivePortal_HttpIsInvalidCapportApiScheme() throws Exception {
1421 runCapportApiInvalidUrlTest("http://capport.example.com");
1422 }
1423
1424 @Test
1425 public void testIsCaptivePortal_FileIsInvalidCapportApiScheme() throws Exception {
1426 runCapportApiInvalidUrlTest("file://localhost/myfile");
1427 }
1428
1429 @Test
1430 public void testIsCaptivePortal_InvalidUrlFormat() throws Exception {
1431 runCapportApiInvalidUrlTest("ThisIsNotAValidUrl");
1432 }
1433
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +09001434 @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001435 public void testIsCaptivePortal_CapportApiNotSupported() throws Exception {
Remi NGUYEN VAN905b0b62020-04-20 17:06:27 +09001436 // Test that on a R+ device, if NetworkStack was compiled without CaptivePortalData support
1437 // (built against Q), NetworkMonitor behaves as expected.
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001438 assumeFalse(CaptivePortalDataShimImpl.isSupported());
1439 setSslException(mHttpsConnection);
1440 setPortal302(mHttpConnection);
1441 setApiContent(mCapportApiConnection, "{'captive': false,"
1442 + "'venue-info-url': '" + TEST_VENUE_INFO_URL + "'}");
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001443 runNetworkTest(makeCapportLPs(), CELL_METERED_CAPABILITIES, VALIDATION_RESULT_PORTAL,
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001444 0 /* probesSucceeded */,
1445 TEST_LOGIN_URL);
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09001446
1447 verify(mCallbacks, never()).notifyCaptivePortalDataChanged(any());
1448 verify(mHttpConnection).getResponseCode();
1449 }
1450
lucaslind4c999f2020-04-09 00:31:21 +08001451 @Test
1452 public void testIsCaptivePortal_HttpsProbeMatchesFailRegex() throws Exception {
1453 setStatus(mHttpsConnection, 200);
1454 setStatus(mHttpConnection, 500);
1455 final String content = "test";
1456 doReturn(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)))
1457 .when(mHttpsConnection).getInputStream();
1458 doReturn(Long.valueOf(content.length())).when(mHttpsConnection).getContentLengthLong();
1459 doReturn(1).when(mResources).getInteger(R.integer.config_min_matches_http_content_length);
1460 doReturn(10).when(mResources).getInteger(
1461 R.integer.config_max_matches_http_content_length);
1462 doReturn("te.t").when(mResources).getString(
1463 R.string.config_network_validation_failed_content_regexp);
1464 runFailedNetworkTest();
1465 }
1466
1467 @Test
1468 public void testIsCaptivePortal_HttpProbeMatchesSuccessRegex() throws Exception {
1469 setStatus(mHttpsConnection, 500);
1470 setStatus(mHttpConnection, 200);
1471 final String content = "test";
1472 doReturn(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)))
1473 .when(mHttpConnection).getInputStream();
1474 doReturn(Long.valueOf(content.length())).when(mHttpConnection).getContentLengthLong();
1475 doReturn(1).when(mResources).getInteger(R.integer.config_min_matches_http_content_length);
1476 doReturn(10).when(mResources).getInteger(
1477 R.integer.config_max_matches_http_content_length);
1478 doReturn("te.t").when(mResources).getString(
1479 R.string.config_network_validation_success_content_regexp);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001480 runPartialConnectivityNetworkTest(
1481 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP);
lucaslind4c999f2020-04-09 00:31:21 +08001482 }
1483
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001484 private void setupFallbackSpec() throws IOException {
1485 setFallbackSpecs("http://example.com@@/@@204@@/@@"
1486 + "@@,@@"
1487 + TEST_OTHER_FALLBACK_URL + "@@/@@30[12]@@/@@https://(www\\.)?google.com/?.*");
1488
1489 setSslException(mHttpsConnection);
1490 setStatus(mHttpConnection, 500);
1491
1492 // Use the 2nd fallback spec
1493 when(mRandom.nextInt()).thenReturn(1);
1494 }
1495
1496 @Test
Chiachang Wangc9a89aa2020-11-13 17:04:35 +08001497 public void testIsCaptivePortal_FallbackSpecIsFail() throws Exception {
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001498 setupFallbackSpec();
1499 set302(mOtherFallbackConnection, "https://www.google.com/test?q=3");
1500
Chiachang Wangc9a89aa2020-11-13 17:04:35 +08001501 runNetworkTest(VALIDATION_RESULT_INVALID,
1502 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_FALLBACK,
1503 null /* redirectUrl */);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001504 verify(mOtherFallbackConnection, times(1)).getResponseCode();
1505 verify(mFallbackConnection, never()).getResponseCode();
1506 }
1507
1508 @Test
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001509 public void testIsCaptivePortal_FallbackSpecIsPortal() throws Exception {
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001510 setupFallbackSpec();
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001511 setPortal302(mOtherFallbackConnection);
1512 runPortalNetworkTest();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001513 }
1514
1515 @Test
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001516 public void testIsCaptivePortal_IgnorePortals() throws Exception {
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001517 setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE);
1518 setSslException(mHttpsConnection);
1519 setPortal302(mHttpConnection);
1520
Chiachang Wang813ee472019-05-23 16:29:30 +08001521 runNoValidationNetworkTest();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001522 }
1523
1524 @Test
Remi NGUYEN VAN943d6342020-03-24 18:22:34 +09001525 public void testIsCaptivePortal_OverriddenHttpsUrlValid() throws Exception {
1526 setDeviceConfig(TEST_URL_EXPIRATION_TIME,
1527 String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(9)));
1528 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL, TEST_OVERRIDE_URL);
1529 setStatus(mTestOverriddenUrlConnection, 204);
1530 setStatus(mHttpConnection, 204);
1531
1532 runValidatedNetworkTest();
1533 verify(mHttpsConnection, never()).getResponseCode();
1534 verify(mTestOverriddenUrlConnection).getResponseCode();
1535 }
1536
1537 @Test
1538 public void testIsCaptivePortal_OverriddenHttpUrlPortal() throws Exception {
1539 setDeviceConfig(TEST_URL_EXPIRATION_TIME,
1540 String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(9)));
1541 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTP_URL, TEST_OVERRIDE_URL);
1542 setStatus(mHttpsConnection, 500);
1543 setPortal302(mTestOverriddenUrlConnection);
1544
1545 runPortalNetworkTest();
1546 verify(mHttpConnection, never()).getResponseCode();
1547 verify(mTestOverriddenUrlConnection).getResponseCode();
1548 }
1549
1550 @Test
1551 public void testIsCaptivePortal_InvalidHttpOverrideUrl() throws Exception {
1552 setDeviceConfig(TEST_URL_EXPIRATION_TIME,
1553 String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(9)));
1554 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTP_URL, TEST_INVALID_OVERRIDE_URL);
1555 setStatus(mHttpsConnection, 500);
1556 setPortal302(mHttpConnection);
1557
1558 runPortalNetworkTest();
1559 verify(mTestOverriddenUrlConnection, never()).getResponseCode();
1560 verify(mHttpConnection).getResponseCode();
1561 }
1562
1563 @Test
1564 public void testIsCaptivePortal_InvalidHttpsOverrideUrl() throws Exception {
1565 setDeviceConfig(TEST_URL_EXPIRATION_TIME,
1566 String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(9)));
1567 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL, TEST_INVALID_OVERRIDE_URL);
1568 setStatus(mHttpsConnection, 204);
1569 setStatus(mHttpConnection, 204);
1570
1571 runValidatedNetworkTest();
1572 verify(mTestOverriddenUrlConnection, never()).getResponseCode();
1573 verify(mHttpsConnection).getResponseCode();
1574 }
1575
1576 @Test
1577 public void testIsCaptivePortal_ExpiredHttpsOverrideUrl() throws Exception {
1578 setDeviceConfig(TEST_URL_EXPIRATION_TIME,
1579 String.valueOf(currentTimeMillis() - TimeUnit.MINUTES.toMillis(1)));
1580 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL, TEST_OVERRIDE_URL);
1581 setStatus(mHttpsConnection, 204);
1582 setStatus(mHttpConnection, 204);
1583
1584 runValidatedNetworkTest();
1585 verify(mTestOverriddenUrlConnection, never()).getResponseCode();
1586 verify(mHttpsConnection).getResponseCode();
1587 }
1588
1589 @Test
1590 public void testIsCaptivePortal_TestHttpUrlExpirationTooLarge() throws Exception {
1591 setDeviceConfig(TEST_URL_EXPIRATION_TIME,
1592 String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(20)));
1593 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTP_URL, TEST_OVERRIDE_URL);
1594 setStatus(mHttpsConnection, 500);
1595 setPortal302(mHttpConnection);
1596
1597 runPortalNetworkTest();
1598 verify(mTestOverriddenUrlConnection, never()).getResponseCode();
1599 verify(mHttpConnection).getResponseCode();
1600 }
1601
1602 @Test
Remi NGUYEN VAN0d8cf032020-08-21 16:49:46 +09001603 public void testIsCaptivePortal_TestUrlsWithUrlOverlays() throws Exception {
1604 setupResourceForMultipleProbes();
1605 doReturn(TEST_HTTPS_URL).when(mResources)
1606 .getString(R.string.config_captive_portal_https_url);
1607 doReturn(TEST_HTTP_URL).when(mResources)
1608 .getString(R.string.config_captive_portal_http_url);
1609
1610 setDeviceConfig(TEST_URL_EXPIRATION_TIME,
1611 String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(9)));
1612 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL, TEST_OVERRIDE_URL);
1613 setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTP_URL, TEST_OVERRIDE_URL);
1614 setStatus(mTestOverriddenUrlConnection, 204);
1615
1616 runValidatedNetworkTest();
1617 verify(mHttpsConnection, never()).getResponseCode();
1618 verify(mHttpConnection, never()).getResponseCode();
1619 verify(mOtherHttpsConnection1, never()).getResponseCode();
1620 verify(mOtherHttpsConnection2, never()).getResponseCode();
1621 verify(mOtherHttpConnection1, never()).getResponseCode();
1622 verify(mOtherHttpConnection2, never()).getResponseCode();
1623
1624 // Used for both HTTP and HTTPS: can be called once (if HTTPS validates first) or twice
1625 verify(mTestOverriddenUrlConnection, atLeastOnce()).getResponseCode();
1626 }
1627
1628 @Test
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001629 public void testIsDataStall_EvaluationDisabled() {
1630 setDataStallEvaluationType(0);
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001631 WrappedNetworkMonitor wrappedMonitor = makeCellMeteredNetworkMonitor();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001632 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
1633 assertFalse(wrappedMonitor.isDataStall());
1634 }
1635
1636 @Test
Cody Kesting782cbfa2020-01-06 15:51:59 -08001637 public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() throws Exception {
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001638 WrappedNetworkMonitor wrappedMonitor = makeCellNotMeteredNetworkMonitor();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001639 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
Chiachang Wangcaa35202019-02-26 11:32:18 +08001640 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001641 assertTrue(wrappedMonitor.isDataStall());
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001642 verify(mCallbacks).notifyDataStallSuspected(
1643 matchDnsDataStallParcelable(DEFAULT_DNS_TIMEOUT_THRESHOLD));
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001644 }
1645
1646 @Test
Cody Kesting782cbfa2020-01-06 15:51:59 -08001647 public void testIsDataStall_EvaluationDnsOnMeteredNetwork() throws Exception {
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001648 WrappedNetworkMonitor wrappedMonitor = makeCellMeteredNetworkMonitor();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001649 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
1650 assertFalse(wrappedMonitor.isDataStall());
1651
Chiachang Wang4fcbe912021-04-26 13:02:22 +08001652 wrappedMonitor.setLastProbeTime(
1653 SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS);
Chiachang Wangcaa35202019-02-26 11:32:18 +08001654 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001655 assertTrue(wrappedMonitor.isDataStall());
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001656 verify(mCallbacks).notifyDataStallSuspected(
1657 matchDnsDataStallParcelable(DEFAULT_DNS_TIMEOUT_THRESHOLD));
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001658 }
1659
1660 @Test
Cody Kesting782cbfa2020-01-06 15:51:59 -08001661 public void testIsDataStall_EvaluationDnsWithDnsTimeoutCount() throws Exception {
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001662 WrappedNetworkMonitor wrappedMonitor = makeCellMeteredNetworkMonitor();
Chiachang Wang4fcbe912021-04-26 13:02:22 +08001663 wrappedMonitor.setLastProbeTime(
1664 SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001665 makeDnsTimeoutEvent(wrappedMonitor, 3);
1666 assertFalse(wrappedMonitor.isDataStall());
1667 // Reset consecutive timeout counts.
1668 makeDnsSuccessEvent(wrappedMonitor, 1);
1669 makeDnsTimeoutEvent(wrappedMonitor, 2);
1670 assertFalse(wrappedMonitor.isDataStall());
1671
1672 makeDnsTimeoutEvent(wrappedMonitor, 3);
1673 assertTrue(wrappedMonitor.isDataStall());
Cody Kestingbfe0e002020-02-04 12:52:44 -08001674
1675 // The expected timeout count is the previous 2 DNS timeouts + the most recent 3 timeouts
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001676 verify(mCallbacks).notifyDataStallSuspected(
1677 matchDnsDataStallParcelable(5 /* timeoutCount */));
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001678
1679 // Set the value to larger than the default dns log size.
1680 setConsecutiveDnsTimeoutThreshold(51);
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001681 wrappedMonitor = makeCellMeteredNetworkMonitor();
Chiachang Wang4fcbe912021-04-26 13:02:22 +08001682 wrappedMonitor.setLastProbeTime(
1683 SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001684 makeDnsTimeoutEvent(wrappedMonitor, 50);
1685 assertFalse(wrappedMonitor.isDataStall());
1686
1687 makeDnsTimeoutEvent(wrappedMonitor, 1);
1688 assertTrue(wrappedMonitor.isDataStall());
Cody Kestingbfe0e002020-02-04 12:52:44 -08001689
1690 // The expected timeout count is the previous 50 DNS timeouts + the most recent timeout
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001691 verify(mCallbacks).notifyDataStallSuspected(
1692 matchDnsDataStallParcelable(51 /* timeoutCount */));
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001693 }
1694
1695 @Test
Chiachang Wangb6dbfb42020-01-21 14:47:34 +08001696 public void testIsDataStall_SkipEvaluateOnValidationNotRequiredNetwork() {
1697 // Make DNS and TCP stall condition satisfied.
1698 setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS | DATA_STALL_EVALUATION_TYPE_TCP);
1699 when(mTstDependencies.isTcpInfoParsingSupported()).thenReturn(true);
1700 when(mTst.getLatestReceivedCount()).thenReturn(0);
1701 when(mTst.isDataStallSuspected()).thenReturn(true);
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001702 final WrappedNetworkMonitor nm = makeMonitor(CELL_NO_INTERNET_CAPABILITIES);
Chiachang Wangb6dbfb42020-01-21 14:47:34 +08001703 nm.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
1704 makeDnsTimeoutEvent(nm, DEFAULT_DNS_TIMEOUT_THRESHOLD);
1705 assertFalse(nm.isDataStall());
1706 }
1707
1708 @Test
Cody Kesting782cbfa2020-01-06 15:51:59 -08001709 public void testIsDataStall_EvaluationDnsWithDnsTimeThreshold() throws Exception {
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001710 // Test dns events happened in valid dns time threshold.
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001711 WrappedNetworkMonitor wrappedMonitor = makeCellMeteredNetworkMonitor();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001712 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
Chiachang Wangcaa35202019-02-26 11:32:18 +08001713 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001714 assertFalse(wrappedMonitor.isDataStall());
Chiachang Wang4fcbe912021-04-26 13:02:22 +08001715 wrappedMonitor.setLastProbeTime(
1716 SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001717 assertTrue(wrappedMonitor.isDataStall());
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001718 verify(mCallbacks).notifyDataStallSuspected(
1719 matchDnsDataStallParcelable(DEFAULT_DNS_TIMEOUT_THRESHOLD));
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001720
1721 // Test dns events happened before valid dns time threshold.
1722 setValidDataStallDnsTimeThreshold(0);
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001723 wrappedMonitor = makeCellMeteredNetworkMonitor();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001724 wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
Chiachang Wangcaa35202019-02-26 11:32:18 +08001725 makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001726 assertFalse(wrappedMonitor.isDataStall());
Chiachang Wang4fcbe912021-04-26 13:02:22 +08001727 wrappedMonitor.setLastProbeTime(
1728 SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001729 assertFalse(wrappedMonitor.isDataStall());
1730 }
1731
1732 @Test
Cody Kesting782cbfa2020-01-06 15:51:59 -08001733 public void testIsDataStall_EvaluationTcp() throws Exception {
Chiachang Wanga5716bf2019-11-20 16:13:07 +08001734 // Evaluate TCP only. Expect ignoring DNS signal.
1735 setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_TCP);
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001736 WrappedNetworkMonitor wrappedMonitor = makeMonitor(CELL_METERED_CAPABILITIES);
Chiachang Wanga5716bf2019-11-20 16:13:07 +08001737 assertFalse(wrappedMonitor.isDataStall());
Chiachang Wang4336b912019-11-26 15:39:22 +08001738 // Packet received.
1739 when(mTstDependencies.isTcpInfoParsingSupported()).thenReturn(true);
1740 when(mTst.getLatestReceivedCount()).thenReturn(5);
1741 // Trigger a tcp event immediately.
1742 setTcpPollingInterval(0);
1743 wrappedMonitor.sendTcpPollingEvent();
Chalard Jean23a06302020-06-26 00:41:00 +09001744 HandlerUtils.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS);
Chiachang Wang4336b912019-11-26 15:39:22 +08001745 assertFalse(wrappedMonitor.isDataStall());
Chiachang Wange797c9b2019-11-28 14:18:47 +08001746
Chiachang Wang4336b912019-11-26 15:39:22 +08001747 when(mTst.getLatestReceivedCount()).thenReturn(0);
1748 when(mTst.isDataStallSuspected()).thenReturn(true);
Chiachang Wanga5716bf2019-11-20 16:13:07 +08001749 // Trigger a tcp event immediately.
1750 setTcpPollingInterval(0);
1751 wrappedMonitor.sendTcpPollingEvent();
Chalard Jean23a06302020-06-26 00:41:00 +09001752 HandlerUtils.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS);
Chiachang Wanga5716bf2019-11-20 16:13:07 +08001753 assertTrue(wrappedMonitor.isDataStall());
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001754 verify(mCallbacks).notifyDataStallSuspected(matchTcpDataStallParcelable());
Chiachang Wanga5716bf2019-11-20 16:13:07 +08001755 }
1756
1757 @Test
Chiachang Wang340d2752020-05-21 17:51:22 +08001758 public void testIsDataStall_EvaluationDnsAndTcp() throws Exception {
1759 setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS | DATA_STALL_EVALUATION_TYPE_TCP);
1760 setupTcpDataStall();
1761 final WrappedNetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES);
Chiachang Wang4fcbe912021-04-26 13:02:22 +08001762 nm.setLastProbeTime(SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS);
Chiachang Wang340d2752020-05-21 17:51:22 +08001763 makeDnsTimeoutEvent(nm, DEFAULT_DNS_TIMEOUT_THRESHOLD);
1764 assertTrue(nm.isDataStall());
1765 verify(mCallbacks).notifyDataStallSuspected(
1766 matchDnsAndTcpDataStallParcelable(DEFAULT_DNS_TIMEOUT_THRESHOLD));
1767
1768 when(mTst.getLatestReceivedCount()).thenReturn(5);
1769 // Trigger a tcp event immediately.
1770 setTcpPollingInterval(0);
1771 nm.sendTcpPollingEvent();
Chalard Jean23a06302020-06-26 00:41:00 +09001772 HandlerUtils.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS);
Chiachang Wang340d2752020-05-21 17:51:22 +08001773 assertFalse(nm.isDataStall());
1774 }
1775
1776 @Test
Chiachang Wange797c9b2019-11-28 14:18:47 +08001777 public void testIsDataStall_DisableTcp() {
1778 // Disable tcp detection with only DNS detect. keep the tcp signal but set to no DNS signal.
1779 setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS);
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001780 WrappedNetworkMonitor wrappedMonitor = makeMonitor(CELL_METERED_CAPABILITIES);
Chiachang Wange797c9b2019-11-28 14:18:47 +08001781 makeDnsSuccessEvent(wrappedMonitor, 1);
1782 wrappedMonitor.sendTcpPollingEvent();
Chalard Jean23a06302020-06-26 00:41:00 +09001783 HandlerUtils.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS);
Chiachang Wange797c9b2019-11-28 14:18:47 +08001784 assertFalse(wrappedMonitor.isDataStall());
1785 verify(mTst, never()).isDataStallSuspected();
1786 verify(mTst, never()).pollSocketsInfo();
1787 }
1788
1789 @Test
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001790 public void testBrokenNetworkNotValidated() throws Exception {
1791 setSslException(mHttpsConnection);
1792 setStatus(mHttpConnection, 500);
1793 setStatus(mFallbackConnection, 404);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001794
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09001795 runFailedNetworkTest();
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001796 }
1797
Remi NGUYEN VAN1b7a0632021-07-07 17:54:09 +09001798 private void doValidationSkippedTest(NetworkCapabilities nc) throws Exception {
Cody Kesting78156e42020-08-03 18:19:56 -07001799 // For S+, the RESULT_SKIPPED bit will be included on networks that both do not require
1800 // validation and for which validation is not performed.
1801 final int validationResult = ShimUtils.isAtLeastS()
1802 ? NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_SKIPPED
1803 : NETWORK_VALIDATION_RESULT_VALID;
Remi NGUYEN VAN1b7a0632021-07-07 17:54:09 +09001804 runNetworkTest(TEST_LINK_PROPERTIES, nc, validationResult,
Cody Kesting78156e42020-08-03 18:19:56 -07001805 0 /* probesSucceeded */, null /* redirectUrl */);
Lorenzo Colitti7f9734f2019-05-09 12:13:54 +09001806 verify(mCleartextDnsNetwork, never()).openConnection(any());
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001807 }
1808
Remi NGUYEN VAN1b7a0632021-07-07 17:54:09 +09001809 @Test
1810 public void testNoInternetCapabilityValidated() throws Exception {
1811 doValidationSkippedTest(CELL_NO_INTERNET_CAPABILITIES);
1812 }
1813
1814 @Test
1815 public void testNoTrustedCapabilityValidated() throws Exception {
Remi NGUYEN VAN38b1a382021-07-15 12:54:25 +09001816 // Cannot use the NetworkCapabilities builder on Q
1817 final NetworkCapabilities nc = new NetworkCapabilities()
Remi NGUYEN VAN1b7a0632021-07-07 17:54:09 +09001818 .addCapability(NET_CAPABILITY_INTERNET)
1819 .removeCapability(NET_CAPABILITY_TRUSTED)
1820 .addTransportType(TRANSPORT_CELLULAR);
1821 if (ShimUtils.isAtLeastS()) {
1822 nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
1823 }
Remi NGUYEN VAN38b1a382021-07-15 12:54:25 +09001824 doValidationSkippedTest(nc);
Remi NGUYEN VAN1b7a0632021-07-07 17:54:09 +09001825 }
1826
1827 @Test
1828 public void testRestrictedCapabilityValidated() throws Exception {
Remi NGUYEN VAN38b1a382021-07-15 12:54:25 +09001829 // Cannot use the NetworkCapabilities builder on Q
1830 final NetworkCapabilities nc = new NetworkCapabilities()
Remi NGUYEN VAN1b7a0632021-07-07 17:54:09 +09001831 .addCapability(NET_CAPABILITY_INTERNET)
1832 .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
1833 .addTransportType(TRANSPORT_CELLULAR);
1834 if (ShimUtils.isAtLeastS()) {
1835 nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
1836 }
Remi NGUYEN VAN38b1a382021-07-15 12:54:25 +09001837 doValidationSkippedTest(nc);
Remi NGUYEN VAN1b7a0632021-07-07 17:54:09 +09001838 }
1839
Benedict Wonge3f53472021-05-12 10:09:00 -07001840 private NetworkCapabilities getVcnUnderlyingCarrierWifiCaps() {
1841 // Must be called from within the test because NOT_VCN_MANAGED is an invalid capability
1842 // value up to Android R. Thus, this must be guarded by an SDK check in tests that use this.
1843 return new NetworkCapabilities.Builder()
1844 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
Remi NGUYEN VAN1b7a0632021-07-07 17:54:09 +09001845 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
Benedict Wonge3f53472021-05-12 10:09:00 -07001846 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
1847 .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
1848 .addCapability(NET_CAPABILITY_INTERNET)
1849 .build();
1850 }
1851
1852 @Test
1853 public void testVcnUnderlyingNetwork() throws Exception {
1854 assumeTrue(ShimUtils.isAtLeastS());
1855 setStatus(mHttpsConnection, 204);
1856 setStatus(mHttpConnection, 204);
1857
1858 final NetworkMonitor nm = runNetworkTest(
1859 TEST_LINK_PROPERTIES, getVcnUnderlyingCarrierWifiCaps(),
1860 NETWORK_VALIDATION_RESULT_VALID,
1861 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS,
1862 null /* redirectUrl */);
1863 assertEquals(NETWORK_VALIDATION_RESULT_VALID,
1864 nm.getEvaluationState().getEvaluationResult());
1865 }
1866
1867 @Test
1868 public void testVcnUnderlyingNetworkBadNetwork() throws Exception {
1869 assumeTrue(ShimUtils.isAtLeastS());
1870 setSslException(mHttpsConnection);
1871 setStatus(mHttpConnection, 500);
1872 setStatus(mFallbackConnection, 404);
1873
1874 final NetworkMonitor nm = runNetworkTest(
1875 TEST_LINK_PROPERTIES, getVcnUnderlyingCarrierWifiCaps(),
1876 VALIDATION_RESULT_INVALID, 0 /* probesSucceeded */, null /* redirectUrl */);
1877 assertEquals(VALIDATION_RESULT_INVALID,
1878 nm.getEvaluationState().getEvaluationResult());
1879 }
1880
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001881 @Test
1882 public void testLaunchCaptivePortalApp() throws Exception {
1883 setSslException(mHttpsConnection);
1884 setPortal302(mHttpConnection);
Chiachang Wang26a626a2020-03-02 17:41:58 +08001885 when(mHttpConnection.getHeaderField(eq("location"))).thenReturn(TEST_LOGIN_URL);
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001886 final NetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES);
1887 notifyNetworkConnected(nm, CELL_METERED_CAPABILITIES);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001888
1889 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
Remi NGUYEN VAN1e3eb372019-02-07 21:29:57 +09001890 .showProvisioningNotification(any(), any());
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001891
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +09001892 assertEquals(1, mRegisteredReceivers.size());
1893
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001894 // Check that startCaptivePortalApp sends the expected intent.
1895 nm.launchCaptivePortalApp();
1896
Remi NGUYEN VANad99e542019-02-13 20:58:59 +09001897 final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
1898 final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class);
1899 verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1))
1900 .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture());
Remi NGUYEN VAN89cd0262019-12-29 22:46:00 +09001901 verify(mNotifier).notifyCaptivePortalValidationPending(networkCaptor.getValue());
Remi NGUYEN VANad99e542019-02-13 20:58:59 +09001902 final Bundle bundle = bundleCaptor.getValue();
1903 final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK);
1904 assertEquals(TEST_NETID, bundleNetwork.netId);
1905 // network is passed both in bundle and as parameter, as the bundle is opaque to the
1906 // framework and only intended for the captive portal app, but the framework needs
1907 // the network to identify the right NetworkMonitor.
1908 assertEquals(TEST_NETID, networkCaptor.getValue().netId);
Chiachang Wang26a626a2020-03-02 17:41:58 +08001909 // Portal URL should be detection URL.
1910 final String redirectUrl = bundle.getString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
1911 assertEquals(TEST_HTTP_URL, redirectUrl);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001912
1913 // Have the app report that the captive portal is dismissed, and check that we revalidate.
1914 setStatus(mHttpsConnection, 204);
1915 setStatus(mHttpConnection, 204);
Remi NGUYEN VANad99e542019-02-13 20:58:59 +09001916
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +09001917 resetCallbacks();
Remi NGUYEN VANad99e542019-02-13 20:58:59 +09001918 nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED);
Chiachang Wang813ee472019-05-23 16:29:30 +08001919 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce())
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001920 .notifyNetworkTestedWithExtras(matchNetworkTestResultParcelable(
1921 NETWORK_VALIDATION_RESULT_VALID,
1922 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP));
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +09001923 assertEquals(0, mRegisteredReceivers.size());
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09001924 }
1925
Chiachang Wang8b5f84a2019-02-22 11:13:07 +08001926 @Test
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09001927 public void testPrivateDnsSuccess() throws Exception {
1928 setStatus(mHttpsConnection, 204);
1929 setStatus(mHttpConnection, 204);
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09001930
Chiachang Wangeb619222019-07-03 20:52:08 +08001931 // Verify dns query only get v6 address.
1932 mFakeDns.setAnswer("dns6.google", new String[]{"2001:db8::53"}, TYPE_AAAA);
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001933 WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
Chiachang Wangeb619222019-07-03 20:52:08 +08001934 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns6.google",
1935 new InetAddress[0]));
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001936 notifyNetworkConnected(wnm, CELL_NOT_METERED_CAPABILITIES);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001937 verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID, PROBES_PRIVDNS_VALID);
lucaslin2ce7dcc2019-10-22 16:59:39 +08001938 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged(
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001939 eq(PROBES_PRIVDNS_VALID), eq(PROBES_PRIVDNS_VALID));
Chiachang Wangeb619222019-07-03 20:52:08 +08001940
1941 // Verify dns query only get v4 address.
1942 resetCallbacks();
1943 mFakeDns.setAnswer("dns4.google", new String[]{"192.0.2.1"}, TYPE_A);
1944 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns4.google",
1945 new InetAddress[0]));
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001946 verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID, PROBES_PRIVDNS_VALID);
lucaslin2ce7dcc2019-10-22 16:59:39 +08001947 // NetworkMonitor will check if the probes has changed or not, if the probes has not
1948 // changed, the callback won't be fired.
1949 verify(mCallbacks, never()).notifyProbeStatusChanged(
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001950 eq(PROBES_PRIVDNS_VALID), eq(PROBES_PRIVDNS_VALID));
Chiachang Wangeb619222019-07-03 20:52:08 +08001951
1952 // Verify dns query get both v4 and v6 address.
1953 resetCallbacks();
1954 mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::54"}, TYPE_AAAA);
1955 mFakeDns.setAnswer("dns.google", new String[]{"192.0.2.3"}, TYPE_A);
1956 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0]));
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001957 verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID, PROBES_PRIVDNS_VALID);
lucaslin2ce7dcc2019-10-22 16:59:39 +08001958 verify(mCallbacks, never()).notifyProbeStatusChanged(
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001959 eq(PROBES_PRIVDNS_VALID), eq(PROBES_PRIVDNS_VALID));
lucaslin2ce7dcc2019-10-22 16:59:39 +08001960 }
1961
1962 @Test
1963 public void testProbeStatusChanged() throws Exception {
1964 // Set no record in FakeDns and expect validation to fail.
1965 setStatus(mHttpsConnection, 204);
1966 setStatus(mHttpConnection, 204);
1967
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001968 WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
lucaslin2ce7dcc2019-10-22 16:59:39 +08001969 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0]));
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001970 wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, CELL_NOT_METERED_CAPABILITIES);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001971 verifyNetworkTested(VALIDATION_RESULT_INVALID,
1972 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS);
1973 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)).notifyProbeStatusChanged(
1974 eq(PROBES_PRIVDNS_VALID), eq(NETWORK_VALIDATION_PROBE_DNS
lucaslin2ce7dcc2019-10-22 16:59:39 +08001975 | NETWORK_VALIDATION_PROBE_HTTPS));
1976 // Fix DNS and retry, expect validation to succeed.
1977 resetCallbacks();
1978 mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::1"}, TYPE_AAAA);
1979
1980 wnm.forceReevaluation(Process.myUid());
1981 // ProbeCompleted should be reset to 0
Chalard Jean23a06302020-06-26 00:41:00 +09001982 HandlerUtils.waitForIdle(wnm.getHandler(), HANDLER_TIMEOUT_MS);
lucaslin2ce7dcc2019-10-22 16:59:39 +08001983 assertEquals(wnm.getEvaluationState().getProbeCompletedResult(), 0);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001984 verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID, PROBES_PRIVDNS_VALID);
1985 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)).notifyProbeStatusChanged(
1986 eq(PROBES_PRIVDNS_VALID), eq(PROBES_PRIVDNS_VALID));
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09001987 }
1988
1989 @Test
1990 public void testPrivateDnsResolutionRetryUpdate() throws Exception {
Chiachang Wangeb619222019-07-03 20:52:08 +08001991 // Set no record in FakeDns and expect validation to fail.
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09001992 setStatus(mHttpsConnection, 204);
1993 setStatus(mHttpConnection, 204);
1994
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001995 WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09001996 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0]));
Chiachang Wangdaf983b2020-05-07 19:20:52 +08001997 wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, CELL_NOT_METERED_CAPABILITIES);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09001998 verifyNetworkTested(VALIDATION_RESULT_INVALID,
1999 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS);
lucaslin2ce7dcc2019-10-22 16:59:39 +08002000 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged(
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002001 eq(PROBES_PRIVDNS_VALID), eq(NETWORK_VALIDATION_PROBE_DNS
lucaslin2ce7dcc2019-10-22 16:59:39 +08002002 | NETWORK_VALIDATION_PROBE_HTTPS));
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09002003
2004 // Fix DNS and retry, expect validation to succeed.
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +09002005 resetCallbacks();
Chiachang Wangeb619222019-07-03 20:52:08 +08002006 mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::1"}, TYPE_AAAA);
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09002007
2008 wnm.forceReevaluation(Process.myUid());
Chiachang Wang813ee472019-05-23 16:29:30 +08002009 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce())
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002010 .notifyNetworkTestedWithExtras(matchNetworkTestResultParcelable(
2011 NETWORK_VALIDATION_RESULT_VALID, PROBES_PRIVDNS_VALID));
lucaslin2ce7dcc2019-10-22 16:59:39 +08002012 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged(
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002013 eq(PROBES_PRIVDNS_VALID), eq(PROBES_PRIVDNS_VALID));
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09002014
2015 // Change configuration to an invalid DNS name, expect validation to fail.
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +09002016 resetCallbacks();
Chiachang Wangeb619222019-07-03 20:52:08 +08002017 mFakeDns.setAnswer("dns.bad", new String[0], TYPE_A);
2018 mFakeDns.setAnswer("dns.bad", new String[0], TYPE_AAAA);
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09002019 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.bad", new InetAddress[0]));
Chiachang Wang813ee472019-05-23 16:29:30 +08002020 // Strict mode hostname resolve fail. Expect only notification for evaluation fail. No probe
2021 // notification.
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002022 verifyNetworkTested(VALIDATION_RESULT_INVALID,
2023 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS);
lucaslin2ce7dcc2019-10-22 16:59:39 +08002024 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged(
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002025 eq(PROBES_PRIVDNS_VALID), eq(NETWORK_VALIDATION_PROBE_DNS
lucaslin2ce7dcc2019-10-22 16:59:39 +08002026 | NETWORK_VALIDATION_PROBE_HTTPS));
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09002027
2028 // Change configuration back to working again, but make private DNS not work.
2029 // Expect validation to fail.
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +09002030 resetCallbacks();
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09002031 mFakeDns.setNonBypassPrivateDnsWorking(false);
Chiachang Wang813ee472019-05-23 16:29:30 +08002032 wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google",
2033 new InetAddress[0]));
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002034 verifyNetworkTested(VALIDATION_RESULT_INVALID,
2035 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS);
lucaslin2ce7dcc2019-10-22 16:59:39 +08002036 // NetworkMonitor will check if the probes has changed or not, if the probes has not
2037 // changed, the callback won't be fired.
2038 verify(mCallbacks, never()).notifyProbeStatusChanged(
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002039 eq(PROBES_PRIVDNS_VALID), eq(NETWORK_VALIDATION_PROBE_DNS
lucaslin2ce7dcc2019-10-22 16:59:39 +08002040 | NETWORK_VALIDATION_PROBE_HTTPS));
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09002041
2042 // Make private DNS work again. Expect validation to succeed.
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +09002043 resetCallbacks();
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09002044 mFakeDns.setNonBypassPrivateDnsWorking(true);
2045 wnm.forceReevaluation(Process.myUid());
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002046 verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID, PROBES_PRIVDNS_VALID);
lucaslin2ce7dcc2019-10-22 16:59:39 +08002047 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged(
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002048 eq(PROBES_PRIVDNS_VALID), eq(PROBES_PRIVDNS_VALID));
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09002049 }
2050
2051 @Test
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002052 public void testDataStall_StallDnsSuspectedAndSendMetricsOnCell() throws Exception {
2053 testDataStall_StallDnsSuspectedAndSendMetrics(NetworkCapabilities.TRANSPORT_CELLULAR,
2054 CELL_METERED_CAPABILITIES);
2055 }
2056
2057 @Test
2058 public void testDataStall_StallDnsSuspectedAndSendMetricsOnWifi() throws Exception {
2059 testDataStall_StallDnsSuspectedAndSendMetrics(NetworkCapabilities.TRANSPORT_WIFI,
2060 WIFI_NOT_METERED_CAPABILITIES);
2061 }
2062
2063 private void testDataStall_StallDnsSuspectedAndSendMetrics(int transport,
2064 NetworkCapabilities nc) throws Exception {
2065 // NM suspects data stall from DNS signal and sends data stall metrics.
2066 final WrappedNetworkMonitor nm = prepareNetworkMonitorForVerifyDataStall(nc);
2067 makeDnsTimeoutEvent(nm, 5);
Chiachang Wangeae41052020-03-25 17:22:55 +08002068 // Trigger a dns signal to start evaluate data stall and upload metrics.
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002069 nm.notifyDnsResponse(RETURN_CODE_DNS_TIMEOUT);
2070 // Verify data sent as expected.
2071 verifySendDataStallDetectionStats(nm, DATA_STALL_EVALUATION_TYPE_DNS, transport);
Chiachang Wang8b5f84a2019-02-22 11:13:07 +08002072 }
2073
2074 @Test
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002075 public void testDataStall_NoStallSuspectedAndSendMetrics() throws Exception {
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002076 final WrappedNetworkMonitor nm = prepareNetworkMonitorForVerifyDataStall(
2077 CELL_METERED_CAPABILITIES);
Chiachang Wangeae41052020-03-25 17:22:55 +08002078 // Setup no data stall dns signal.
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002079 makeDnsTimeoutEvent(nm, 3);
2080 assertFalse(nm.isDataStall());
Chiachang Wangeae41052020-03-25 17:22:55 +08002081 // Trigger a dns signal to start evaluate data stall.
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002082 nm.notifyDnsResponse(RETURN_CODE_DNS_SUCCESS);
Chiachang Wangeae41052020-03-25 17:22:55 +08002083 verify(mDependencies, never()).writeDataStallDetectionStats(any(), any());
Chiachang Wang8b5f84a2019-02-22 11:13:07 +08002084 }
Chiachang Wangcaa35202019-02-26 11:32:18 +08002085
2086 @Test
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002087 public void testDataStall_StallTcpSuspectedAndSendMetricsOnCell() throws Exception {
Chiachang Wang2e099cc2020-05-21 16:46:05 +08002088 testDataStall_StallTcpSuspectedAndSendMetrics(CELL_METERED_CAPABILITIES);
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002089 }
Chiachang Wangcaa35202019-02-26 11:32:18 +08002090
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002091 @Test
2092 public void testDataStall_StallTcpSuspectedAndSendMetricsOnWifi() throws Exception {
Chiachang Wang2e099cc2020-05-21 16:46:05 +08002093 testDataStall_StallTcpSuspectedAndSendMetrics(WIFI_NOT_METERED_CAPABILITIES);
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002094 }
Chiachang Wangcaa35202019-02-26 11:32:18 +08002095
Chiachang Wang2e099cc2020-05-21 16:46:05 +08002096 private void testDataStall_StallTcpSuspectedAndSendMetrics(NetworkCapabilities nc)
2097 throws Exception {
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002098 assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q));
Chiachang Wang57218af2020-05-28 15:55:33 +08002099 setupTcpDataStall();
Chiachang Wang0cbaa8d2020-07-21 09:55:58 +08002100 setTcpPollingInterval(0);
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002101 // NM suspects data stall from TCP signal and sends data stall metrics.
2102 setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_TCP);
2103 final WrappedNetworkMonitor nm = prepareNetworkMonitorForVerifyDataStall(nc);
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002104 // Trigger a tcp event immediately.
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002105 nm.sendTcpPollingEvent();
Chiachang Wang2e099cc2020-05-21 16:46:05 +08002106 // Allow only one transport type in the context of this test for simplification.
2107 final int[] transports = nc.getTransportTypes();
2108 assertEquals(1, transports.length);
2109 verifySendDataStallDetectionStats(nm, DATA_STALL_EVALUATION_TYPE_TCP, transports[0]);
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002110 }
2111
2112 private WrappedNetworkMonitor prepareNetworkMonitorForVerifyDataStall(NetworkCapabilities nc)
2113 throws Exception {
2114 // Connect a VALID network to simulate the data stall detection because data stall
2115 // evaluation will only start from validated state.
2116 setStatus(mHttpsConnection, 204);
2117 final WrappedNetworkMonitor nm;
Chiachang Wang2e099cc2020-05-21 16:46:05 +08002118 // Allow only one transport type in the context of this test for simplification.
2119 if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
2120 nm = makeCellMeteredNetworkMonitor();
2121 } else if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
2122 nm = makeWifiNotMeteredNetworkMonitor();
2123 setupTestWifiInfo();
2124 } else {
2125 nm = null;
2126 fail("Undefined transport type");
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002127 }
2128 nm.notifyNetworkConnected(TEST_LINK_PROPERTIES, nc);
2129 verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID,
2130 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS);
Chiachang Wang4fcbe912021-04-26 13:02:22 +08002131 nm.setLastProbeTime(SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS);
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002132 return nm;
2133 }
2134
2135 private void setupTcpDataStall() {
2136 when(mTstDependencies.isTcpInfoParsingSupported()).thenReturn(true);
2137 when(mTst.getLatestReceivedCount()).thenReturn(0);
2138 when(mTst.getLatestPacketFailPercentage()).thenReturn(TEST_TCP_FAIL_RATE);
2139 when(mTst.getSentSinceLastRecv()).thenReturn(TEST_TCP_PACKET_COUNT);
2140 when(mTst.isDataStallSuspected()).thenReturn(true);
2141 when(mTst.pollSocketsInfo()).thenReturn(true);
2142 }
2143
2144 private void verifySendDataStallDetectionStats(WrappedNetworkMonitor nm, int evalType,
2145 int transport) {
2146 // Verify data sent as expectated.
2147 final ArgumentCaptor<CaptivePortalProbeResult> probeResultCaptor =
2148 ArgumentCaptor.forClass(CaptivePortalProbeResult.class);
2149 final ArgumentCaptor<DataStallDetectionStats> statsCaptor =
2150 ArgumentCaptor.forClass(DataStallDetectionStats.class);
2151 verify(mDependencies, timeout(HANDLER_TIMEOUT_MS).times(1))
2152 .writeDataStallDetectionStats(statsCaptor.capture(), probeResultCaptor.capture());
Chiachang Wange7524072021-04-26 08:36:00 +08002153 // Ensure probe will not stop due to rate-limiting mechanism.
Chiachang Wang4fcbe912021-04-26 13:02:22 +08002154 nm.setLastProbeTime(SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS);
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002155 assertTrue(nm.isDataStall());
2156 assertTrue(probeResultCaptor.getValue().isSuccessful());
2157 verifyTestDataStallDetectionStats(evalType, transport, statsCaptor.getValue());
2158 }
2159
2160 private void verifyTestDataStallDetectionStats(int evalType, int transport,
2161 DataStallDetectionStats stats) {
2162 assertEquals(transport, stats.mNetworkType);
2163 switch (transport) {
2164 case NetworkCapabilities.TRANSPORT_WIFI:
2165 assertArrayEquals(makeTestWifiDataNano(), stats.mWifiInfo);
2166 // Expedient way to check stats.mCellularInfo contains the neutral byte array that
2167 // is sent to represent a lack of data, as stats.mCellularInfo is not supposed to
2168 // contain null.
2169 assertArrayEquals(DataStallDetectionStats.emptyCellDataIfNull(null),
2170 stats.mCellularInfo);
2171 break;
2172 case NetworkCapabilities.TRANSPORT_CELLULAR:
2173 // Expedient way to check stats.mWifiInfo contains the neutral byte array that is
2174 // sent to represent a lack of data, as stats.mWifiInfo is not supposed to contain
2175 // null.
2176 assertArrayEquals(DataStallDetectionStats.emptyWifiInfoIfNull(null),
2177 stats.mWifiInfo);
2178 assertArrayEquals(makeTestCellDataNano(), stats.mCellularInfo);
2179 break;
2180 default:
2181 // Add other cases.
2182 fail("Unexpected transport type");
2183 }
2184
2185 assertEquals(evalType, stats.mEvaluationType);
2186 if ((evalType & DATA_STALL_EVALUATION_TYPE_TCP) != 0) {
2187 assertEquals(TEST_TCP_FAIL_RATE, stats.mTcpFailRate);
2188 assertEquals(TEST_TCP_PACKET_COUNT, stats.mTcpSentSinceLastRecv);
2189 } else {
2190 assertEquals(DataStallDetectionStats.UNSPECIFIED_TCP_FAIL_RATE, stats.mTcpFailRate);
2191 assertEquals(DataStallDetectionStats.UNSPECIFIED_TCP_PACKETS_COUNT,
2192 stats.mTcpSentSinceLastRecv);
2193 }
2194
2195 if ((evalType & DATA_STALL_EVALUATION_TYPE_DNS) != 0) {
2196 assertArrayEquals(stats.mDns, makeTestDnsTimeoutNano(DEFAULT_DNS_TIMEOUT_THRESHOLD));
2197 } else {
2198 assertArrayEquals(stats.mDns, makeTestDnsTimeoutNano(0 /* times */));
2199 }
2200 }
2201
2202 private DataStallDetectionStats makeTestDataStallDetectionStats(int evaluationType,
2203 int transportType) {
2204 final DataStallDetectionStats.Builder stats = new DataStallDetectionStats.Builder()
2205 .setEvaluationType(evaluationType)
2206 .setNetworkType(transportType);
2207 switch (transportType) {
2208 case NetworkCapabilities.TRANSPORT_CELLULAR:
2209 stats.setCellData(TelephonyManager.NETWORK_TYPE_LTE /* radioType */,
Chiachang Wangcaa35202019-02-26 11:32:18 +08002210 true /* roaming */,
2211 TEST_MCCMNC /* networkMccmnc */,
2212 TEST_MCCMNC /* simMccmnc */,
2213 CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN /* signalStrength */);
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002214 break;
2215 case NetworkCapabilities.TRANSPORT_WIFI:
2216 setupTestWifiInfo();
2217 stats.setWiFiData(mWifiInfo);
2218 break;
2219 default:
2220 break;
2221 }
Chiachang Wangcaa35202019-02-26 11:32:18 +08002222
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002223 if ((evaluationType & DATA_STALL_EVALUATION_TYPE_TCP) != 0) {
2224 generateTestTcpStats(stats);
2225 }
Chiachang Wangcaa35202019-02-26 11:32:18 +08002226
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002227 if ((evaluationType & DATA_STALL_EVALUATION_TYPE_DNS) != 0) {
2228 generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
2229 }
2230
2231 return stats.build();
2232 }
2233
2234 private byte[] makeTestDnsTimeoutNano(int timeoutCount) {
2235 // Make a expected nano dns message.
2236 final DnsEvent event = new DnsEvent();
2237 event.dnsReturnCode = new int[timeoutCount];
2238 event.dnsTime = new long[timeoutCount];
2239 Arrays.fill(event.dnsReturnCode, RETURN_CODE_DNS_TIMEOUT);
2240 Arrays.fill(event.dnsTime, TEST_ELAPSED_TIME_MS);
2241 return MessageNano.toByteArray(event);
2242 }
2243
2244 private byte[] makeTestCellDataNano() {
2245 final CellularData data = new CellularData();
2246 data.ratType = DataStallEventProto.RADIO_TECHNOLOGY_LTE;
2247 data.networkMccmnc = TEST_MCCMNC;
2248 data.simMccmnc = TEST_MCCMNC;
2249 data.isRoaming = true;
2250 data.signalStrength = 0;
2251 return MessageNano.toByteArray(data);
2252 }
2253
2254 private byte[] makeTestWifiDataNano() {
2255 final WifiData data = new WifiData();
2256 data.wifiBand = DataStallEventProto.AP_BAND_2GHZ;
2257 data.signalStrength = TEST_SIGNAL_STRENGTH;
2258 return MessageNano.toByteArray(data);
2259 }
2260
2261 private void setupTestWifiInfo() {
Chiachang Wangcaa35202019-02-26 11:32:18 +08002262 when(mWifi.getConnectionInfo()).thenReturn(mWifiInfo);
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002263 when(mWifiInfo.getRssi()).thenReturn(TEST_SIGNAL_STRENGTH);
2264 // Set to 2.4G band. Map to DataStallEventProto.AP_BAND_2GHZ proto definition.
2265 when(mWifiInfo.getFrequency()).thenReturn(2450);
2266 }
Chiachang Wangcaa35202019-02-26 11:32:18 +08002267
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002268 private void testDataStallMetricsWithCellular(int evalType) {
2269 testDataStallMetrics(evalType, NetworkCapabilities.TRANSPORT_CELLULAR);
2270 }
Chiachang Wangcaa35202019-02-26 11:32:18 +08002271
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002272 private void testDataStallMetricsWithWiFi(int evalType) {
2273 testDataStallMetrics(evalType, NetworkCapabilities.TRANSPORT_WIFI);
2274 }
2275
2276 private void testDataStallMetrics(int evalType, int transportType) {
2277 setDataStallEvaluationType(evalType);
2278 final NetworkCapabilities nc = new NetworkCapabilities()
2279 .addTransportType(transportType)
2280 .addCapability(NET_CAPABILITY_INTERNET);
2281 final WrappedNetworkMonitor wrappedMonitor = makeMonitor(nc);
2282 setupTestWifiInfo();
2283 final DataStallDetectionStats stats =
2284 makeTestDataStallDetectionStats(evalType, transportType);
2285 assertEquals(wrappedMonitor.buildDataStallDetectionStats(transportType, evalType), stats);
2286
2287 if ((evalType & DATA_STALL_EVALUATION_TYPE_TCP) != 0) {
2288 verify(mTst, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()).getLatestPacketFailPercentage();
2289 } else {
2290 verify(mTst, never()).getLatestPacketFailPercentage();
2291 }
2292 }
2293
2294 @Test
2295 public void testCollectDataStallMetrics_DnsWithCellular() {
2296 testDataStallMetricsWithCellular(DATA_STALL_EVALUATION_TYPE_DNS);
2297 }
2298
2299 @Test
2300 public void testCollectDataStallMetrics_DnsWithWiFi() {
2301 testDataStallMetricsWithWiFi(DATA_STALL_EVALUATION_TYPE_DNS);
2302 }
2303
2304 @Test
2305 public void testCollectDataStallMetrics_TcpWithCellular() {
2306 assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q));
2307 testDataStallMetricsWithCellular(DATA_STALL_EVALUATION_TYPE_TCP);
2308 }
2309
2310 @Test
2311 public void testCollectDataStallMetrics_TcpWithWiFi() {
2312 assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q));
2313 testDataStallMetricsWithWiFi(DATA_STALL_EVALUATION_TYPE_TCP);
2314 }
2315
2316 @Test
2317 public void testCollectDataStallMetrics_TcpAndDnsWithWifi() {
2318 assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q));
2319 testDataStallMetricsWithWiFi(
2320 DATA_STALL_EVALUATION_TYPE_TCP | DATA_STALL_EVALUATION_TYPE_DNS);
2321 }
2322
2323 @Test
2324 public void testCollectDataStallMetrics_TcpAndDnsWithCellular() {
2325 assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q));
2326 testDataStallMetricsWithCellular(
2327 DATA_STALL_EVALUATION_TYPE_TCP | DATA_STALL_EVALUATION_TYPE_DNS);
Chiachang Wangcaa35202019-02-26 11:32:18 +08002328 }
2329
lucaslinb0573962019-03-12 13:08:03 +08002330 @Test
2331 public void testIgnoreHttpsProbe() throws Exception {
2332 setSslException(mHttpsConnection);
2333 setStatus(mHttpConnection, 204);
Chiachang Wang813ee472019-05-23 16:29:30 +08002334 // Expect to send HTTP, HTTPS, FALLBACK probe and evaluation result notifications to CS.
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002335 final NetworkMonitor nm = runNetworkTest(NETWORK_VALIDATION_RESULT_PARTIAL,
2336 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP,
2337 null /* redirectUrl */);
lucaslinb0573962019-03-12 13:08:03 +08002338
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +09002339 resetCallbacks();
lucaslind01ea622019-03-20 18:21:59 +08002340 nm.setAcceptPartialConnectivity();
Chiachang Wang813ee472019-05-23 16:29:30 +08002341 // Expect to update evaluation result notifications to CS.
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002342 verifyNetworkTested(NETWORK_VALIDATION_RESULT_PARTIAL | NETWORK_VALIDATION_RESULT_VALID,
2343 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP);
lucaslinb0573962019-03-12 13:08:03 +08002344 }
2345
2346 @Test
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002347 public void testIsPartialConnectivity() throws Exception {
lucaslinb0573962019-03-12 13:08:03 +08002348 setStatus(mHttpsConnection, 500);
2349 setStatus(mHttpConnection, 204);
2350 setStatus(mFallbackConnection, 500);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002351 runPartialConnectivityNetworkTest(
2352 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP);
Chiachang Wangc9a89aa2020-11-13 17:04:35 +08002353 }
lucaslinb0573962019-03-12 13:08:03 +08002354
Chiachang Wangc9a89aa2020-11-13 17:04:35 +08002355 @Test
2356 public void testIsCaptivePortal_OnlyFallbackSucceed() throws Exception {
lucaslinb0573962019-03-12 13:08:03 +08002357 setStatus(mHttpsConnection, 500);
2358 setStatus(mHttpConnection, 500);
2359 setStatus(mFallbackConnection, 204);
Chiachang Wangc9a89aa2020-11-13 17:04:35 +08002360 runNetworkTest(VALIDATION_RESULT_INVALID,
2361 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_FALLBACK,
2362 null /* redirectUrl */);
lucaslinb0573962019-03-12 13:08:03 +08002363 }
2364
Lorenzo Colitti171cfd22019-04-18 13:44:32 +09002365 private void assertIpAddressArrayEquals(String[] expected, InetAddress[] actual) {
2366 String[] actualStrings = new String[actual.length];
2367 for (int i = 0; i < actual.length; i++) {
2368 actualStrings[i] = actual[i].getHostAddress();
2369 }
2370 assertArrayEquals("Array of IP addresses differs", expected, actualStrings);
2371 }
2372
2373 @Test
2374 public void testSendDnsProbeWithTimeout() throws Exception {
Chiachang Wangdaf983b2020-05-07 19:20:52 +08002375 WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
Lorenzo Colitti171cfd22019-04-18 13:44:32 +09002376 final int shortTimeoutMs = 200;
Chiachang Wangeb619222019-07-03 20:52:08 +08002377 // v6 only.
Lorenzo Colitti171cfd22019-04-18 13:44:32 +09002378 String[] expected = new String[]{"2001:db8::"};
Chiachang Wangeb619222019-07-03 20:52:08 +08002379 mFakeDns.setAnswer("www.google.com", expected, TYPE_AAAA);
Lorenzo Colitti171cfd22019-04-18 13:44:32 +09002380 InetAddress[] actual = wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs);
2381 assertIpAddressArrayEquals(expected, actual);
Chiachang Wangeb619222019-07-03 20:52:08 +08002382 // v4 only.
2383 expected = new String[]{"192.0.2.1"};
2384 mFakeDns.setAnswer("www.android.com", expected, TYPE_A);
2385 actual = wnm.sendDnsProbeWithTimeout("www.android.com", shortTimeoutMs);
2386 assertIpAddressArrayEquals(expected, actual);
2387 // Both v4 & v6.
2388 expected = new String[]{"192.0.2.1", "2001:db8::"};
2389 mFakeDns.setAnswer("www.googleapis.com", new String[]{"192.0.2.1"}, TYPE_A);
2390 mFakeDns.setAnswer("www.googleapis.com", new String[]{"2001:db8::"}, TYPE_AAAA);
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09002391 actual = wnm.sendDnsProbeWithTimeout("www.googleapis.com", shortTimeoutMs);
Lorenzo Colitti171cfd22019-04-18 13:44:32 +09002392 assertIpAddressArrayEquals(expected, actual);
Chiachang Wangeb619222019-07-03 20:52:08 +08002393 // Clear DNS response.
2394 mFakeDns.setAnswer("www.android.com", new String[0], TYPE_A);
Lorenzo Colitti171cfd22019-04-18 13:44:32 +09002395 try {
Chiachang Wangeb619222019-07-03 20:52:08 +08002396 actual = wnm.sendDnsProbeWithTimeout("www.android.com", shortTimeoutMs);
Lorenzo Colitti171cfd22019-04-18 13:44:32 +09002397 fail("No DNS results, expected UnknownHostException");
2398 } catch (UnknownHostException e) {
2399 }
2400
Chiachang Wangeb619222019-07-03 20:52:08 +08002401 mFakeDns.setAnswer("www.android.com", null, TYPE_A);
2402 mFakeDns.setAnswer("www.android.com", null, TYPE_AAAA);
Lorenzo Colitti171cfd22019-04-18 13:44:32 +09002403 try {
Chiachang Wangeb619222019-07-03 20:52:08 +08002404 wnm.sendDnsProbeWithTimeout("www.android.com", shortTimeoutMs);
Lorenzo Colitti171cfd22019-04-18 13:44:32 +09002405 fail("DNS query timed out, expected UnknownHostException");
2406 } catch (UnknownHostException e) {
2407 }
2408 }
2409
Chiachang Wang813ee472019-05-23 16:29:30 +08002410 @Test
2411 public void testNotifyNetwork_WithforceReevaluation() throws Exception {
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002412 setValidProbes();
Chiachang Wang813ee472019-05-23 16:29:30 +08002413 final NetworkMonitor nm = runValidatedNetworkTest();
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002414 // Verify forceReevaluation will not reset the validation result but only probe result until
Chiachang Wang813ee472019-05-23 16:29:30 +08002415 // getting the validation result.
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +09002416 resetCallbacks();
Chiachang Wang813ee472019-05-23 16:29:30 +08002417 setSslException(mHttpsConnection);
2418 setStatus(mHttpConnection, 500);
2419 setStatus(mFallbackConnection, 204);
2420 nm.forceReevaluation(Process.myUid());
Chiachang Wang813ee472019-05-23 16:29:30 +08002421 // Expect to send HTTP, HTTPs, FALLBACK and evaluation results.
Chiachang Wanga1a987f2020-12-30 11:49:32 +08002422 verifyNetworkTested(VALIDATION_RESULT_INVALID,
Chiachang Wangc9a89aa2020-11-13 17:04:35 +08002423 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_FALLBACK,
2424 null /* redirectUrl */);
Chiachang Wanga1a987f2020-12-30 11:49:32 +08002425 HandlerUtils.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS);
Cody Kesting176bce72020-01-20 18:09:59 -08002426 }
2427
2428 @Test
2429 public void testNotifyNetwork_NotifyNetworkTestedOldInterfaceVersion() throws Exception {
2430 // Use old interface version so notifyNetworkTested is used over
2431 // notifyNetworkTestedWithExtras
Cody Kesting2b26c8b2020-02-10 09:48:56 -08002432 resetCallbacks(4);
Cody Kesting176bce72020-01-20 18:09:59 -08002433
2434 // Trigger Network validation
2435 setStatus(mHttpsConnection, 204);
2436 setStatus(mHttpConnection, 204);
Chiachang Wangdaf983b2020-05-07 19:20:52 +08002437 final NetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES);
2438 nm.notifyNetworkConnected(TEST_LINK_PROPERTIES, CELL_METERED_CAPABILITIES);
Cody Kesting176bce72020-01-20 18:09:59 -08002439
2440 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS))
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002441 .notifyNetworkTested(eq(NETWORK_VALIDATION_RESULT_VALID
2442 | NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS),
2443 eq(null) /* redirectUrl */);
Chiachang Wang813ee472019-05-23 16:29:30 +08002444 }
2445
2446 @Test
Chiachang Wang26a626a2020-03-02 17:41:58 +08002447 public void testDismissPortalInValidatedNetworkEnabledOsSupported() throws Exception {
Lorenzo Colitti0d08b442020-04-27 11:49:39 +09002448 assumeTrue(ShimUtils.isAtLeastR());
Chiachang Wang220428b2020-05-29 10:03:41 +08002449 testDismissPortalInValidatedNetworkEnabled(TEST_LOGIN_URL, TEST_LOGIN_URL);
2450 }
2451
2452 @Test
2453 public void testDismissPortalInValidatedNetworkEnabledOsSupported_NullLocationUrl()
2454 throws Exception {
2455 assumeTrue(ShimUtils.isAtLeastR());
2456 testDismissPortalInValidatedNetworkEnabled(TEST_HTTP_URL, null /* locationUrl */);
2457 }
2458
2459 @Test
2460 public void testDismissPortalInValidatedNetworkEnabledOsSupported_InvalidLocationUrl()
2461 throws Exception {
2462 assumeTrue(ShimUtils.isAtLeastR());
2463 testDismissPortalInValidatedNetworkEnabled(TEST_HTTP_URL, TEST_RELATIVE_URL);
Chiachang Wang26a626a2020-03-02 17:41:58 +08002464 }
2465
2466 @Test
2467 public void testDismissPortalInValidatedNetworkEnabledOsNotSupported() throws Exception {
Lorenzo Colitti0d08b442020-04-27 11:49:39 +09002468 assumeFalse(ShimUtils.isAtLeastR());
Chiachang Wang220428b2020-05-29 10:03:41 +08002469 testDismissPortalInValidatedNetworkEnabled(TEST_HTTP_URL, TEST_LOGIN_URL);
Chiachang Wang26a626a2020-03-02 17:41:58 +08002470 }
2471
Chiachang Wang220428b2020-05-29 10:03:41 +08002472 private void testDismissPortalInValidatedNetworkEnabled(String expectedUrl, String locationUrl)
2473 throws Exception {
Chiachang Wang26a626a2020-03-02 17:41:58 +08002474 setDismissPortalInValidatedNetwork(true);
2475 setSslException(mHttpsConnection);
2476 setPortal302(mHttpConnection);
Chiachang Wang220428b2020-05-29 10:03:41 +08002477 when(mHttpConnection.getHeaderField(eq("location"))).thenReturn(locationUrl);
Chiachang Wangdaf983b2020-05-07 19:20:52 +08002478 final NetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES);
2479 notifyNetworkConnected(nm, CELL_METERED_CAPABILITIES);
Chiachang Wang26a626a2020-03-02 17:41:58 +08002480
2481 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
2482 .showProvisioningNotification(any(), any());
2483
2484 assertEquals(1, mRegisteredReceivers.size());
2485 // Check that startCaptivePortalApp sends the expected intent.
2486 nm.launchCaptivePortalApp();
2487
2488 final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
2489 final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class);
2490 verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1))
2491 .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture());
2492 verify(mNotifier).notifyCaptivePortalValidationPending(networkCaptor.getValue());
2493 final Bundle bundle = bundleCaptor.getValue();
2494 final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK);
2495 assertEquals(TEST_NETID, bundleNetwork.netId);
2496 // Network is passed both in bundle and as parameter, as the bundle is opaque to the
2497 // framework and only intended for the captive portal app, but the framework needs
2498 // the network to identify the right NetworkMonitor.
2499 assertEquals(TEST_NETID, networkCaptor.getValue().netId);
2500 // Portal URL should be redirect URL.
2501 final String redirectUrl = bundle.getString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
Chiachang Wang220428b2020-05-29 10:03:41 +08002502 assertEquals(expectedUrl, redirectUrl);
Chiachang Wang26a626a2020-03-02 17:41:58 +08002503 }
2504
2505 @Test
Chiachang Wang813ee472019-05-23 16:29:30 +08002506 public void testEvaluationState_clearProbeResults() throws Exception {
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002507 setValidProbes();
Chiachang Wang813ee472019-05-23 16:29:30 +08002508 final NetworkMonitor nm = runValidatedNetworkTest();
2509 nm.getEvaluationState().clearProbeResults();
2510 // Verify probe results are all reset and only evaluation result left.
2511 assertEquals(NETWORK_VALIDATION_RESULT_VALID,
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002512 nm.getEvaluationState().getEvaluationResult());
2513 assertEquals(0, nm.getEvaluationState().getProbeResults());
Chiachang Wang813ee472019-05-23 16:29:30 +08002514 }
2515
2516 @Test
2517 public void testEvaluationState_reportProbeResult() throws Exception {
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002518 setValidProbes();
Chiachang Wang813ee472019-05-23 16:29:30 +08002519 final NetworkMonitor nm = runValidatedNetworkTest();
2520
Remi NGUYEN VANb4af1302019-06-11 16:17:46 +09002521 resetCallbacks();
Chiachang Wang813ee472019-05-23 16:29:30 +08002522
Chiachang Wang717099f2020-03-23 11:31:39 +08002523 nm.reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP,
Chiachang Wang5696b902020-03-23 15:09:57 +08002524 CaptivePortalProbeResult.success(1 << PROBE_HTTP));
Cody Kesting176bce72020-01-20 18:09:59 -08002525 // Verify result should be appended and notifyNetworkTestedWithExtras callback is triggered
2526 // once.
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002527 assertEquals(NETWORK_VALIDATION_RESULT_VALID,
2528 nm.getEvaluationState().getEvaluationResult());
2529 assertEquals(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS
2530 | NETWORK_VALIDATION_PROBE_HTTP, nm.getEvaluationState().getProbeResults());
Chiachang Wang813ee472019-05-23 16:29:30 +08002531
Chiachang Wang717099f2020-03-23 11:31:39 +08002532 nm.reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP,
Chiachang Wang5696b902020-03-23 15:09:57 +08002533 CaptivePortalProbeResult.failed(1 << PROBE_HTTP));
Chiachang Wang813ee472019-05-23 16:29:30 +08002534 // Verify DNS probe result should not be cleared.
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002535 assertEquals(NETWORK_VALIDATION_PROBE_DNS,
2536 nm.getEvaluationState().getProbeResults() & NETWORK_VALIDATION_PROBE_DNS);
Chiachang Wang813ee472019-05-23 16:29:30 +08002537 }
2538
2539 @Test
2540 public void testEvaluationState_reportEvaluationResult() throws Exception {
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002541 setStatus(mHttpsConnection, 500);
2542 setStatus(mHttpConnection, 204);
2543 final NetworkMonitor nm = runNetworkTest(NETWORK_VALIDATION_RESULT_PARTIAL,
2544 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP,
Chiachang Wang813ee472019-05-23 16:29:30 +08002545 null /* redirectUrl */);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002546
2547 nm.getEvaluationState().reportEvaluationResult(NETWORK_VALIDATION_RESULT_VALID,
2548 null /* redirectUrl */);
2549 verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID,
2550 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP);
Chiachang Wang813ee472019-05-23 16:29:30 +08002551
2552 nm.getEvaluationState().reportEvaluationResult(
2553 NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_PARTIAL,
2554 null /* redirectUrl */);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002555 verifyNetworkTested(
2556 NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_PARTIAL,
2557 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP);
Chiachang Wang813ee472019-05-23 16:29:30 +08002558
2559 nm.getEvaluationState().reportEvaluationResult(VALIDATION_RESULT_INVALID,
2560 TEST_REDIRECT_URL);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002561 verifyNetworkTested(VALIDATION_RESULT_INVALID,
2562 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP,
2563 TEST_REDIRECT_URL);
Chiachang Wang813ee472019-05-23 16:29:30 +08002564 }
2565
Remi NGUYEN VAN2d909a72019-12-24 18:15:52 +09002566 @Test
2567 public void testExtractCharset() {
2568 assertEquals(StandardCharsets.UTF_8, extractCharset(null));
2569 assertEquals(StandardCharsets.UTF_8, extractCharset("text/html;charset=utf-8"));
2570 assertEquals(StandardCharsets.UTF_8, extractCharset("text/html;charset=UtF-8"));
2571 assertEquals(StandardCharsets.UTF_8, extractCharset("text/html; Charset=\"utf-8\""));
2572 assertEquals(StandardCharsets.UTF_8, extractCharset("image/png"));
2573 assertEquals(StandardCharsets.UTF_8, extractCharset("Text/HTML;"));
2574 assertEquals(StandardCharsets.UTF_8, extractCharset("multipart/form-data; boundary=-aa*-"));
2575 assertEquals(StandardCharsets.UTF_8, extractCharset("text/plain;something=else"));
2576 assertEquals(StandardCharsets.UTF_8, extractCharset("text/plain;charset=ImNotACharset"));
2577
2578 assertEquals(StandardCharsets.ISO_8859_1, extractCharset("text/plain; CharSeT=ISO-8859-1"));
2579 assertEquals(Charset.forName("Shift_JIS"), extractCharset("text/plain;charset=Shift_JIS"));
2580 assertEquals(Charset.forName("Windows-1251"), extractCharset(
2581 "text/plain;charset=Windows-1251 ; somethingelse"));
2582 }
2583
2584 @Test
2585 public void testReadAsString() throws IOException {
2586 final String repeatedString = "1aテスト-?";
2587 // Infinite stream repeating characters
2588 class TestInputStream extends InputStream {
2589 private final byte[] mBytes = repeatedString.getBytes(StandardCharsets.UTF_8);
2590 private int mPosition = -1;
2591
2592 @Override
2593 public int read() {
2594 mPosition = (mPosition + 1) % mBytes.length;
2595 return mBytes[mPosition];
2596 }
2597 }
2598
2599 final String readString = NetworkMonitor.readAsString(new TestInputStream(),
2600 1500 /* maxLength */, StandardCharsets.UTF_8);
2601
2602 assertEquals(1500, readString.length());
2603 for (int i = 0; i < readString.length(); i++) {
2604 assertEquals(repeatedString.charAt(i % repeatedString.length()), readString.charAt(i));
2605 }
2606 }
2607
lucaslind4c999f2020-04-09 00:31:21 +08002608 @Test
2609 public void testReadAsString_StreamShorterThanLimit() throws Exception {
Chiachang Wangdaf983b2020-05-07 19:20:52 +08002610 final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
lucaslin139738f2020-04-17 12:22:55 +08002611 final byte[] content = "The HTTP response code is 200 but it is not a captive portal."
2612 .getBytes(StandardCharsets.UTF_8);
2613 assertEquals(new String(content), wnm.readAsString(new ByteArrayInputStream(content),
2614 content.length, StandardCharsets.UTF_8));
2615 // Test the case that the stream ends earlier than the limit.
2616 assertEquals(new String(content), wnm.readAsString(new ByteArrayInputStream(content),
2617 content.length + 10, StandardCharsets.UTF_8));
lucaslind4c999f2020-04-09 00:31:21 +08002618 }
2619
Chiachang Wang80be4412020-04-14 11:52:10 +08002620 @Test
2621 public void testMultipleProbesOnPortalNetwork() throws Exception {
2622 setupResourceForMultipleProbes();
2623 // One of the http probes is portal, then result is portal.
2624 setPortal302(mOtherHttpConnection1);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002625 runPortalNetworkTest();
Chiachang Wang80be4412020-04-14 11:52:10 +08002626 // Get conclusive result from one of the HTTP probe. Expect to create 2 HTTP and 2 HTTPS
Remi NGUYEN VANeb5bfa92020-05-01 10:43:12 +09002627 // probes as resource configuration, but the portal can be detected before other probes
2628 // start.
2629 verify(mCleartextDnsNetwork, atMost(4)).openConnection(any());
2630 verify(mCleartextDnsNetwork, atLeastOnce()).openConnection(any());
2631 verify(mOtherHttpConnection1).getResponseCode();
Chiachang Wang80be4412020-04-14 11:52:10 +08002632 }
2633
2634 @Test
2635 public void testMultipleProbesOnValidNetwork() throws Exception {
2636 setupResourceForMultipleProbes();
2637 // One of the https probes succeeds, then it's validated.
2638 setStatus(mOtherHttpsConnection2, 204);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002639 runValidatedNetworkTest();
Chiachang Wang80be4412020-04-14 11:52:10 +08002640 // Get conclusive result from one of the HTTPS probe. Expect to create 2 HTTP and 2 HTTPS
Remi NGUYEN VANeb5bfa92020-05-01 10:43:12 +09002641 // probes as resource configuration, but the network may validate from the HTTPS probe
2642 // before other probes start.
2643 verify(mCleartextDnsNetwork, atMost(4)).openConnection(any());
2644 verify(mCleartextDnsNetwork, atLeastOnce()).openConnection(any());
2645 verify(mOtherHttpsConnection2).getResponseCode();
Chiachang Wang80be4412020-04-14 11:52:10 +08002646 }
2647
2648 @Test
Remi NGUYEN VANeb5bfa92020-05-01 10:43:12 +09002649 public void testMultipleProbesOnInValidNetworkForPrioritizedResource() throws Exception {
Chiachang Wang80be4412020-04-14 11:52:10 +08002650 setupResourceForMultipleProbes();
2651 // The configuration resource is prioritized. Only use configurations from resource.(i.e
2652 // Only configuration for mOtherHttpsConnection2, mOtherHttpsConnection2,
2653 // mOtherHttpConnection2, mOtherHttpConnection2 will affect the result.)
2654 // Configure mHttpsConnection is no-op.
2655 setStatus(mHttpsConnection, 204);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002656 runFailedNetworkTest();
Chiachang Wang80be4412020-04-14 11:52:10 +08002657 // No conclusive result from both HTTP and HTTPS probes. Expect to create 2 HTTP and 2 HTTPS
Remi NGUYEN VANeb5bfa92020-05-01 10:43:12 +09002658 // probes as resource configuration. All probes are expected to have been run because this
2659 // network is set to never validate (no probe has a success or portal result), so NM tests
2660 // all probes to completion.
Chiachang Wang80be4412020-04-14 11:52:10 +08002661 verify(mCleartextDnsNetwork, times(4)).openConnection(any());
Remi NGUYEN VANeb5bfa92020-05-01 10:43:12 +09002662 verify(mHttpsConnection, never()).getResponseCode();
Chiachang Wang80be4412020-04-14 11:52:10 +08002663 }
2664
2665 @Test
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002666 public void testMultipleProbesOnInValidNetwork() throws Exception {
Chiachang Wang80be4412020-04-14 11:52:10 +08002667 setupResourceForMultipleProbes();
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002668 runFailedNetworkTest();
Chiachang Wang80be4412020-04-14 11:52:10 +08002669 // No conclusive result from both HTTP and HTTPS probes. Expect to create 2 HTTP and 2 HTTPS
2670 // probes as resource configuration.
2671 verify(mCleartextDnsNetwork, times(4)).openConnection(any());
2672 }
2673
Hai Shalome8fe1232021-02-08 11:47:15 -08002674 @Test
2675 public void testIsCaptivePortal_FromExternalSource() throws Exception {
2676 assumeTrue(CaptivePortalDataShimImpl.isSupported());
2677 assumeTrue(ShimUtils.isAtLeastS());
2678 when(mDependencies.isFeatureEnabled(any(), eq(NAMESPACE_CONNECTIVITY),
2679 eq(DISMISS_PORTAL_IN_VALIDATED_NETWORK), anyBoolean())).thenReturn(true);
2680 final NetworkMonitor monitor = makeMonitor(WIFI_NOT_METERED_CAPABILITIES);
Lorenzo Colittia83f6532021-02-21 17:51:09 +09002681
Hai Shalome8fe1232021-02-08 11:47:15 -08002682 NetworkInformationShim networkShim = NetworkInformationShimImpl.newInstance();
Lorenzo Colittia83f6532021-02-21 17:51:09 +09002683 CaptivePortalDataShim captivePortalData = new CaptivePortalDataShimImpl(
2684 new CaptivePortalData.Builder().setCaptive(true).build());
2685 final LinkProperties linkProperties = new LinkProperties(TEST_LINK_PROPERTIES);
2686 networkShim.setCaptivePortalData(linkProperties, captivePortalData);
Hai Shalome8fe1232021-02-08 11:47:15 -08002687 CaptivePortalDataShim captivePortalDataShim =
2688 networkShim.getCaptivePortalData(linkProperties);
2689
2690 try {
2691 // Set up T&C captive portal info from Passpoint
2692 captivePortalData = captivePortalDataShim.withPasspointInfo(TEST_FRIENDLY_NAME,
2693 Uri.parse(TEST_VENUE_INFO_URL), Uri.parse(TEST_LOGIN_URL));
2694 } catch (UnsupportedApiLevelException e) {
2695 // Minimum API level for this test is 31
2696 return;
2697 }
2698
Lorenzo Colittia83f6532021-02-21 17:51:09 +09002699 networkShim.setCaptivePortalData(linkProperties, captivePortalData);
Hai Shalome8fe1232021-02-08 11:47:15 -08002700 monitor.notifyLinkPropertiesChanged(linkProperties);
2701 final NetworkCapabilities networkCapabilities =
2702 new NetworkCapabilities(WIFI_NOT_METERED_CAPABILITIES);
2703 monitor.notifyNetworkConnected(linkProperties, networkCapabilities);
2704 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
2705 .showProvisioningNotification(any(), any());
2706 assertEquals(1, mRegisteredReceivers.size());
2707 verifyNetworkTested(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, TEST_LOGIN_URL);
2708
2709 // Force reevaluation and confirm that the network is still captive
2710 HandlerUtils.waitForIdle(monitor.getHandler(), HANDLER_TIMEOUT_MS);
2711 resetCallbacks();
2712 monitor.forceReevaluation(Process.myUid());
2713 assertEquals(monitor.getEvaluationState().getProbeCompletedResult(), 0);
2714 verifyNetworkTested(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, TEST_LOGIN_URL);
2715
2716 // Check that startCaptivePortalApp sends the expected intent.
2717 monitor.launchCaptivePortalApp();
2718
2719 verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1)).startCaptivePortalApp(
2720 argThat(network -> TEST_NETID == network.netId),
2721 argThat(bundle -> bundle.getString(
2722 ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL).equals(TEST_LOGIN_URL)
2723 && TEST_NETID == ((Network) bundle.getParcelable(
2724 ConnectivityManager.EXTRA_NETWORK)).netId));
2725 }
2726
Pavan Kumar M2fbc8462021-04-28 11:20:25 +05302727 @Test
2728 public void testOemPaidNetworkValidated() throws Exception {
2729 setValidProbes();
2730
2731 final NetworkMonitor nm = runNetworkTest(TEST_LINK_PROPERTIES,
2732 WIFI_OEM_PAID_CAPABILITIES,
2733 NETWORK_VALIDATION_RESULT_VALID,
2734 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS,
2735 null /* redirectUrl */);
2736 assertEquals(NETWORK_VALIDATION_RESULT_VALID,
2737 nm.getEvaluationState().getEvaluationResult());
2738 }
2739
2740 @Test
2741 public void testOemPaidNetwork_AllProbesFailed() throws Exception {
2742 setSslException(mHttpsConnection);
2743 setStatus(mHttpConnection, 500);
2744 setStatus(mFallbackConnection, 404);
2745
2746 runNetworkTest(TEST_LINK_PROPERTIES,
2747 WIFI_OEM_PAID_CAPABILITIES,
2748 VALIDATION_RESULT_INVALID, 0 /* probesSucceeded */, null /* redirectUrl */);
2749 }
2750
2751 @Test
2752 public void testOemPaidNetworkNoInternetCapabilityValidated() throws Exception {
2753 setSslException(mHttpsConnection);
2754 setStatus(mHttpConnection, 500);
2755 setStatus(mFallbackConnection, 404);
2756
2757 final NetworkCapabilities networkCapabilities =
2758 new NetworkCapabilities(WIFI_OEM_PAID_CAPABILITIES);
2759 networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
Cody Kesting78156e42020-08-03 18:19:56 -07002760
2761 final int validationResult = ShimUtils.isAtLeastS()
2762 ? NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_SKIPPED
2763 : NETWORK_VALIDATION_RESULT_VALID;
Pavan Kumar M2fbc8462021-04-28 11:20:25 +05302764 runNetworkTest(TEST_LINK_PROPERTIES, networkCapabilities,
Cody Kesting78156e42020-08-03 18:19:56 -07002765 validationResult, 0 /* probesSucceeded */, null /* redirectUrl */);
Pavan Kumar M2fbc8462021-04-28 11:20:25 +05302766
2767 verify(mCleartextDnsNetwork, never()).openConnection(any());
2768 verify(mHttpsConnection, never()).getResponseCode();
2769 verify(mHttpConnection, never()).getResponseCode();
2770 verify(mFallbackConnection, never()).getResponseCode();
2771 }
2772
2773 @Test
2774 public void testOemPaidNetwork_CaptivePortalNotLaunched() throws Exception {
2775 setSslException(mHttpsConnection);
2776 setStatus(mFallbackConnection, 404);
2777 setPortal302(mHttpConnection);
2778
2779 runNetworkTest(TEST_LINK_PROPERTIES, WIFI_OEM_PAID_CAPABILITIES,
2780 VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */,
2781 TEST_LOGIN_URL);
2782
2783 verify(mCallbacks, never()).showProvisioningNotification(any(), any());
2784 }
2785
Chiachang Wang80be4412020-04-14 11:52:10 +08002786 private void setupResourceForMultipleProbes() {
2787 // Configure the resource to send multiple probe.
2788 when(mResources.getStringArray(R.array.config_captive_portal_https_urls))
2789 .thenReturn(TEST_HTTPS_URLS);
2790 when(mResources.getStringArray(R.array.config_captive_portal_http_urls))
2791 .thenReturn(TEST_HTTP_URLS);
2792 }
2793
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002794 private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
2795 for (int i = 0; i < count; i++) {
2796 wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
2797 RETURN_CODE_DNS_TIMEOUT);
2798 }
2799 }
2800
2801 private void makeDnsSuccessEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
2802 for (int i = 0; i < count; i++) {
2803 wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
2804 RETURN_CODE_DNS_SUCCESS);
2805 }
2806 }
2807
Chiachang Wang899d5962019-03-08 14:34:19 +08002808 private DataStallDetectionStats makeEmptyDataStallDetectionStats() {
2809 return new DataStallDetectionStats.Builder().build();
2810 }
2811
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002812 private void setDataStallEvaluationType(int type) {
Chiachang Wang9a87f802019-04-08 19:06:21 +08002813 when(mDependencies.getDeviceConfigPropertyInt(any(),
2814 eq(CONFIG_DATA_STALL_EVALUATION_TYPE), anyInt())).thenReturn(type);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002815 }
2816
2817 private void setMinDataStallEvaluateInterval(int time) {
Chiachang Wang9a87f802019-04-08 19:06:21 +08002818 when(mDependencies.getDeviceConfigPropertyInt(any(),
2819 eq(CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL), anyInt())).thenReturn(time);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002820 }
2821
2822 private void setValidDataStallDnsTimeThreshold(int time) {
Chiachang Wang9a87f802019-04-08 19:06:21 +08002823 when(mDependencies.getDeviceConfigPropertyInt(any(),
2824 eq(CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD), anyInt())).thenReturn(time);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002825 }
2826
2827 private void setConsecutiveDnsTimeoutThreshold(int num) {
Chiachang Wang9a87f802019-04-08 19:06:21 +08002828 when(mDependencies.getDeviceConfigPropertyInt(any(),
2829 eq(CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD), anyInt())).thenReturn(num);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002830 }
2831
Chiachang Wanga5716bf2019-11-20 16:13:07 +08002832 private void setTcpPollingInterval(int time) {
Chiachang Wang0cbaa8d2020-07-21 09:55:58 +08002833 doReturn(time).when(mDependencies).getDeviceConfigPropertyInt(any(),
2834 eq(CONFIG_DATA_STALL_TCP_POLLING_INTERVAL), anyInt());
Chiachang Wanga5716bf2019-11-20 16:13:07 +08002835 }
2836
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002837 private void setFallbackUrl(String url) {
2838 when(mDependencies.getSetting(any(),
2839 eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL), any())).thenReturn(url);
2840 }
2841
2842 private void setOtherFallbackUrls(String urls) {
Chiachang Wang79a6da32019-04-17 17:00:54 +08002843 when(mDependencies.getDeviceConfigProperty(any(),
2844 eq(CAPTIVE_PORTAL_OTHER_FALLBACK_URLS), any())).thenReturn(urls);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002845 }
2846
2847 private void setFallbackSpecs(String specs) {
Chiachang Wang79a6da32019-04-17 17:00:54 +08002848 when(mDependencies.getDeviceConfigProperty(any(),
2849 eq(CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002850 }
2851
2852 private void setCaptivePortalMode(int mode) {
2853 when(mDependencies.getSetting(any(),
2854 eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())).thenReturn(mode);
2855 }
2856
Chiachang Wang26a626a2020-03-02 17:41:58 +08002857 private void setDismissPortalInValidatedNetwork(boolean enabled) {
2858 when(mDependencies.isFeatureEnabled(any(), any(),
2859 eq(DISMISS_PORTAL_IN_VALIDATED_NETWORK), anyBoolean())).thenReturn(enabled);
2860 }
2861
Remi NGUYEN VAN943d6342020-03-24 18:22:34 +09002862 private void setDeviceConfig(String key, String value) {
2863 doReturn(value).when(mDependencies).getDeviceConfigProperty(eq(NAMESPACE_CONNECTIVITY),
2864 eq(key), any() /* defaultValue */);
2865 }
2866
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002867 private NetworkMonitor runPortalNetworkTest() throws RemoteException {
2868 final NetworkMonitor nm = runNetworkTest(VALIDATION_RESULT_PORTAL,
2869 0 /* probesSucceeded */, TEST_LOGIN_URL);
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +09002870 assertEquals(1, mRegisteredReceivers.size());
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002871 return nm;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002872 }
2873
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002874 private NetworkMonitor runNoValidationNetworkTest() throws RemoteException {
2875 final NetworkMonitor nm = runNetworkTest(NETWORK_VALIDATION_RESULT_VALID,
2876 0 /* probesSucceeded */, null /* redirectUrl */);
Chiachang Wang813ee472019-05-23 16:29:30 +08002877 assertEquals(0, mRegisteredReceivers.size());
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002878 return nm;
Chiachang Wang813ee472019-05-23 16:29:30 +08002879 }
2880
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002881 private NetworkMonitor runFailedNetworkTest() throws RemoteException {
2882 final NetworkMonitor nm = runNetworkTest(
2883 VALIDATION_RESULT_INVALID, 0 /* probesSucceeded */, null /* redirectUrl */);
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +09002884 assertEquals(0, mRegisteredReceivers.size());
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002885 return nm;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002886 }
2887
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002888 private NetworkMonitor runPartialConnectivityNetworkTest(int probesSucceeded)
2889 throws RemoteException {
2890 final NetworkMonitor nm = runNetworkTest(NETWORK_VALIDATION_RESULT_PARTIAL,
2891 probesSucceeded, null /* redirectUrl */);
Lorenzo Colitti37ee46f2019-04-08 21:51:41 +09002892 assertEquals(0, mRegisteredReceivers.size());
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002893 return nm;
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002894 }
2895
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002896 private NetworkMonitor runValidatedNetworkTest() throws RemoteException {
2897 // Expect to send HTTPS and evaluation results.
2898 return runNetworkTest(NETWORK_VALIDATION_RESULT_VALID,
2899 NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS,
2900 null /* redirectUrl */);
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09002901 }
2902
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002903 private NetworkMonitor runNetworkTest(int testResult, int probesSucceeded, String redirectUrl)
2904 throws RemoteException {
Chiachang Wangdaf983b2020-05-07 19:20:52 +08002905 return runNetworkTest(TEST_LINK_PROPERTIES, CELL_METERED_CAPABILITIES, testResult,
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002906 probesSucceeded, redirectUrl);
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09002907 }
2908
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09002909 private NetworkMonitor runNetworkTest(LinkProperties lp, NetworkCapabilities nc,
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002910 int testResult, int probesSucceeded, String redirectUrl) throws RemoteException {
Lorenzo Colitticfc1abc2019-04-28 00:55:19 +09002911 final NetworkMonitor monitor = makeMonitor(nc);
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09002912 monitor.notifyNetworkConnected(lp, nc);
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002913 verifyNetworkTested(testResult, probesSucceeded, redirectUrl);
Chalard Jean23a06302020-06-26 00:41:00 +09002914 HandlerUtils.waitForIdle(monitor.getHandler(), HANDLER_TIMEOUT_MS);
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09002915
2916 return monitor;
2917 }
2918
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002919 private void verifyNetworkTested(int testResult, int probesSucceeded) throws RemoteException {
2920 verifyNetworkTested(testResult, probesSucceeded, null /* redirectUrl */);
2921 }
2922
2923 private void verifyNetworkTested(int testResult, int probesSucceeded, String redirectUrl)
2924 throws RemoteException {
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09002925 try {
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002926 verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)).notifyNetworkTestedWithExtras(
2927 matchNetworkTestResultParcelable(testResult, probesSucceeded, redirectUrl));
2928 } catch (AssertionFailedError e) {
2929 // Capture the callbacks up to now to give a better error message
2930 final ArgumentCaptor<NetworkTestResultParcelable> captor =
2931 ArgumentCaptor.forClass(NetworkTestResultParcelable.class);
2932
2933 // Call verify() again to verify the same method call verified by the previous verify
2934 // call which failed, but this time use a captor to log the exact parcel sent by
2935 // NetworkMonitor.
2936 // This assertion will fail if notifyNetworkTested was not called at all.
2937 verify(mCallbacks).notifyNetworkTestedWithExtras(captor.capture());
2938
2939 final NetworkTestResultParcelable lastResult = captor.getValue();
2940 fail(String.format("notifyNetworkTestedWithExtras was not called with the "
2941 + "expected result within timeout. "
2942 + "Expected result %d, probes succeeded %d, redirect URL %s, "
2943 + "last result was (%d, %d, %s).",
2944 testResult, probesSucceeded, redirectUrl,
2945 lastResult.result, lastResult.probesSucceeded, lastResult.redirectUrl));
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09002946 }
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09002947 }
Remi NGUYEN VANdc6e6402019-03-27 15:42:53 +09002948
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09002949 private void notifyNetworkConnected(NetworkMonitor nm, NetworkCapabilities nc) {
2950 nm.notifyNetworkConnected(TEST_LINK_PROPERTIES, nc);
lucaslinb0573962019-03-12 13:08:03 +08002951 }
2952
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002953 private void setSslException(HttpURLConnection connection) throws IOException {
2954 doThrow(new SSLHandshakeException("Invalid cert")).when(connection).getResponseCode();
2955 }
2956
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002957 private void setValidProbes() throws IOException {
2958 setStatus(mHttpsConnection, 204);
2959 setStatus(mHttpConnection, 204);
2960 }
2961
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002962 private void set302(HttpURLConnection connection, String location) throws IOException {
2963 setStatus(connection, 302);
2964 doReturn(location).when(connection).getHeaderField(LOCATION_HEADER);
2965 }
2966
2967 private void setPortal302(HttpURLConnection connection) throws IOException {
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002968 set302(connection, TEST_LOGIN_URL);
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002969 }
2970
Remi NGUYEN VAN2fa48c22019-12-09 16:40:02 +09002971 private void setApiContent(HttpURLConnection connection, String content) throws IOException {
2972 setStatus(connection, 200);
2973 final Map<String, List<String>> headerFields = new HashMap<>();
2974 headerFields.put(
2975 CONTENT_TYPE_HEADER, singletonList("application/captive+json;charset=UTF-8"));
2976 doReturn(headerFields).when(connection).getHeaderFields();
2977 doReturn(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)))
2978 .when(connection).getInputStream();
2979 }
2980
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09002981 private void setStatus(HttpURLConnection connection, int status) throws IOException {
2982 doReturn(status).when(connection).getResponseCode();
2983 }
Chiachang Wangcaa35202019-02-26 11:32:18 +08002984
2985 private void generateTimeoutDnsEvent(DataStallDetectionStats.Builder stats, int num) {
2986 for (int i = 0; i < num; i++) {
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002987 stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, TEST_ELAPSED_TIME_MS /* timeMs */);
Chiachang Wangcaa35202019-02-26 11:32:18 +08002988 }
2989 }
Chiachang Wang813ee472019-05-23 16:29:30 +08002990
Chiachang Wang9af1cb52020-05-20 15:14:46 +08002991 private void generateTestTcpStats(DataStallDetectionStats.Builder stats) {
2992 when(mTst.getLatestPacketFailPercentage()).thenReturn(TEST_TCP_FAIL_RATE);
2993 when(mTst.getSentSinceLastRecv()).thenReturn(TEST_TCP_PACKET_COUNT);
2994 stats.setTcpFailRate(TEST_TCP_FAIL_RATE).setTcpSentSinceLastRecv(TEST_TCP_PACKET_COUNT);
2995 }
2996
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09002997 private NetworkTestResultParcelable matchNetworkTestResultParcelable(final int result,
2998 final int probesSucceeded) {
2999 return matchNetworkTestResultParcelable(result, probesSucceeded, null /* redirectUrl */);
Cody Kesting176bce72020-01-20 18:09:59 -08003000 }
3001
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09003002 private NetworkTestResultParcelable matchNetworkTestResultParcelable(final int result,
3003 final int probesSucceeded, String redirectUrl) {
3004 // TODO: also verify probesAttempted
3005 return argThat(p -> p.result == result && p.probesSucceeded == probesSucceeded
3006 && Objects.equals(p.redirectUrl, redirectUrl));
Cody Kesting176bce72020-01-20 18:09:59 -08003007 }
3008
Chiachang Wang340d2752020-05-21 17:51:22 +08003009 private DataStallReportParcelable matchDnsAndTcpDataStallParcelable(final int timeoutCount) {
3010 return argThat(p ->
3011 (p.detectionMethod & ConstantsShim.DETECTION_METHOD_DNS_EVENTS) != 0
3012 && (p.detectionMethod & ConstantsShim.DETECTION_METHOD_TCP_METRICS) != 0
3013 && p.dnsConsecutiveTimeouts == timeoutCount);
3014 }
3015
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09003016 private DataStallReportParcelable matchDnsDataStallParcelable(final int timeoutCount) {
Chiachang Wang340d2752020-05-21 17:51:22 +08003017 return argThat(p -> (p.detectionMethod & ConstantsShim.DETECTION_METHOD_DNS_EVENTS) != 0
Remi NGUYEN VAN0402afc2020-04-15 18:56:59 +09003018 && p.dnsConsecutiveTimeouts == timeoutCount);
3019 }
3020
3021 private DataStallReportParcelable matchTcpDataStallParcelable() {
Chiachang Wang340d2752020-05-21 17:51:22 +08003022 return argThat(p -> (p.detectionMethod & ConstantsShim.DETECTION_METHOD_TCP_METRICS) != 0);
Cody Kesting176bce72020-01-20 18:09:59 -08003023 }
Remi NGUYEN VAN5daa3702018-12-27 16:43:56 +09003024}
3025