Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.cts.verifier.net;
     18 
     19 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
     20 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
     21 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
     22 
     23 import static com.android.cts.verifier.net.MultiNetworkConnectivityTestActivity.ValidatorState
     24         .COMPLETED;
     25 import static com.android.cts.verifier.net.MultiNetworkConnectivityTestActivity.ValidatorState
     26         .NOT_STARTED;
     27 import static com.android.cts.verifier.net.MultiNetworkConnectivityTestActivity.ValidatorState
     28         .STARTED;
     29 import static com.android.cts.verifier.net.MultiNetworkConnectivityTestActivity.ValidatorState
     30         .WAITING_FOR_USER_INPUT;
     31 
     32 import android.content.BroadcastReceiver;
     33 import android.content.Context;
     34 import android.content.Intent;
     35 import android.content.IntentFilter;
     36 import android.net.ConnectivityManager;
     37 import android.net.ConnectivityManager.NetworkCallback;
     38 import android.net.DhcpInfo;
     39 import android.net.Network;
     40 import android.net.NetworkCapabilities;
     41 import android.net.NetworkInfo;
     42 import android.net.NetworkRequest;
     43 import android.net.wifi.SupplicantState;
     44 import android.net.wifi.WifiConfiguration;
     45 import android.net.wifi.WifiInfo;
     46 import android.net.wifi.WifiManager;
     47 import android.os.Build;
     48 import android.os.Bundle;
     49 import android.os.Handler;
     50 import android.os.Looper;
     51 import android.telephony.TelephonyManager;
     52 import android.text.Editable;
     53 import android.text.TextUtils;
     54 import android.text.TextWatcher;
     55 import android.util.Log;
     56 import android.widget.Button;
     57 import android.widget.EditText;
     58 import android.widget.TextView;
     59 
     60 import com.android.cts.verifier.PassFailButtons;
     61 import com.android.cts.verifier.R;
     62 
     63 import java.util.List;
     64 
     65 /**
     66  * A CTS verifier to ensure that when an app calls WifiManager#enableNetwork,
     67  * - When the wifi network does not have internet connectivity, the device should
     68  * not disable other forms or connectivity, for example cellular.
     69  * - When the wifi network that the phone connects to loses connectivity, then
     70  * other forms of connectivity are restored, for example cellular when the phone
     71  * detects that the Wifi network doesn't have internet.
     72  */
     73 public class MultiNetworkConnectivityTestActivity extends PassFailButtons.Activity {
     74     public static final String TAG = "MultinetworkTest";
     75     public static final int ENABLE_DISABLE_WIFI_DELAY_MS = 3000;
     76     public static final int WIFI_NETWORK_CONNECT_TIMEOUT_MS = 45000;
     77     public static final int WIFI_NETWORK_CONNECT_TO_BE_ACTIVE_MS = 25000;
     78     public static final int CELLULAR_NETWORK_CONNECT_TIMEOUT_MS = 45000;
     79     public static final int CELLULAR_NETWORK_RESTORE_TIMEOUT_MS = 15000;
     80     public static final int CELLULAR_NETWORK_RESTORE_AFTER_WIFI_INTERNET_LOST_TIMEOUT_MS = 60000;
     81 
     82     /**
     83      * Called by the validator test when it has different states.
     84      */
     85     private interface MultinetworkTestCallback {
     86         /** Notify test has started */
     87         void testStarted();
     88 
     89         /** Show / display progress using the message in progressMessage */
     90         void testProgress(int progressMessageResourceId);
     91 
     92         /** Test completed for the validator */
     93         void testCompleted(MultiNetworkValidator validator);
     94     }
     95 
     96     enum ValidatorState {
     97         NOT_STARTED,
     98         STARTED,
     99         WAITING_FOR_USER_INPUT,
    100         COMPLETED,
    101     }
    102 
    103     private final Handler mMainHandler = new Handler(Looper.getMainLooper());
    104     // Used only for posting bugs / debugging.
    105     private final BroadcastReceiver mMultiNetConnectivityReceiver = new BroadcastReceiver() {
    106         @Override
    107         public void onReceive(Context context, Intent intent) {
    108             Log.d(TAG, "Action " + intent.getAction());
    109             if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
    110                 NetworkInfo networkInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
    111                 Log.d(TAG, "New network state " + networkInfo.getState());
    112             } else if (intent.getAction().equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) {
    113                 SupplicantState state = intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE);
    114                 Log.d(TAG, "New supplicant state. " + state.name());
    115                 Log.d(TAG, "Is connected to expected wifi AP. " +
    116                         isConnectedToExpectedWifiNetwork());
    117             }
    118         }
    119     };
    120     private final MultinetworkTestCallback mMultinetworkTestCallback =
    121             new MultinetworkTestCallback() {
    122 
    123                 @Override
    124                 public void testStarted() {
    125                     mTestInfoView.setText(R.string.multinetwork_connectivity_test_running);
    126                 }
    127 
    128                 @Override
    129                 public void testProgress(int progressMessageResourceId) {
    130                     mTestInfoView.setText(progressMessageResourceId);
    131                 }
    132 
    133                 @Override
    134                 public void testCompleted(MultiNetworkValidator validator) {
    135                     if (validator == mMultiNetworkValidators[mMultiNetworkValidators.length
    136                             - 1]) {
    137                         // Done all tests.
    138                         boolean passed = true;
    139                         for (MultiNetworkValidator multiNetworkValidator :
    140                                 mMultiNetworkValidators) {
    141                             passed = passed && multiNetworkValidator.mTestResult;
    142                         }
    143                         setTestResultAndFinish(passed);
    144                     } else if (!validator.mTestResult) {
    145                         setTestResultAndFinish(false);
    146                     } else {
    147                         for (int i = 0; i < mMultiNetworkValidators.length; i++) {
    148                             if (mMultiNetworkValidators[i] == validator) {
    149                                 mCurrentValidator = mMultiNetworkValidators[i + 1];
    150                                 mTestNameView.setText(mCurrentValidator.mTestDescription);
    151                                 mCurrentValidator.startTest();
    152                                 break;
    153                             }
    154                         }
    155                     }
    156                 }
    157             };
    158     private final MultiNetworkValidator[] mMultiNetworkValidators = {
    159             new ConnectToWifiWithNoInternetValidator(
    160                     R.string.multinetwork_connectivity_test_1_desc),
    161             new ConnectToWifiWithIntermittentInternetValidator(
    162                     R.string.multinetwork_connectivity_test_2_desc)
    163     };
    164     private final Runnable mTimeToCompletionRunnable = new Runnable() {
    165         @Override
    166         public void run() {
    167             mSecondsToCompletion--;
    168             if (mSecondsToCompletion > 0) {
    169                 mStartButton.setText("" + mSecondsToCompletion);
    170                 mMainHandler.postDelayed(this, 1000);
    171             }
    172         }
    173     };
    174 
    175     // User interface elements.
    176     private Button mStartButton;
    177     private TextView mTestNameView;
    178     private TextView mTestInfoView;
    179     private EditText mAccessPointSsidEditText;
    180     private EditText mPskEditText;
    181 
    182     // Current state memebers.
    183     private MultiNetworkValidator mCurrentValidator;
    184     private int mSecondsToCompletion;
    185     private String mAccessPointSsid = "";
    186     private String mPskValue = "";
    187     private ConnectivityManager mConnectivityManager;
    188     private WifiManager mWifiManager;
    189 
    190     private int mRecordedWifiConfiguration = -1;
    191 
    192     @Override
    193     protected void onCreate(Bundle savedInstanceState) {
    194         super.onCreate(savedInstanceState);
    195         mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    196         mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    197         recordCurrentWifiState();
    198         setupUserInterface();
    199         setupBroadcastReceivers();
    200     }
    201 
    202     @Override
    203     protected void onResume() {
    204         super.onResume();
    205         setupCurrentTestStateOnResume();
    206     }
    207 
    208     @Override
    209     protected void onDestroy() {
    210         super.onDestroy();
    211         destroyBroadcastReceivers();
    212         restoreOriginalWifiState();
    213     }
    214 
    215     private void recordCurrentWifiState() {
    216         if (!mWifiManager.isWifiEnabled()) {
    217             return;
    218         }
    219         WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
    220         if (wifiInfo != null && SupplicantState.COMPLETED.equals(wifiInfo.getSupplicantState())) {
    221             mRecordedWifiConfiguration = wifiInfo.getNetworkId();
    222         }
    223     }
    224 
    225     private void restoreOriginalWifiState() {
    226         if (mRecordedWifiConfiguration >= 0) {
    227             mWifiManager.enableNetwork(mRecordedWifiConfiguration, true);
    228         }
    229     }
    230 
    231     private void setupUserInterface() {
    232         setContentView(R.layout.multinetwork_connectivity);
    233         setInfoResources(
    234                 R.string.multinetwork_connectivity_test,
    235                 R.string.multinetwork_connectivity_test_instructions,
    236                 -1);
    237         mStartButton = findViewById(R.id.start_multinet_btn);
    238         mTestNameView = findViewById(R.id.current_test);
    239         mTestInfoView = findViewById(R.id.test_progress_info);
    240         mAccessPointSsidEditText = findViewById(R.id.test_ap_ssid);
    241         mPskEditText = findViewById(R.id.test_ap_psk);
    242         mAccessPointSsidEditText.addTextChangedListener(new TextWatcher() {
    243             @Override
    244             public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
    245 
    246             @Override
    247             public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
    248 
    249             @Override
    250             public void afterTextChanged(Editable editable) {
    251                 mAccessPointSsid = editable.toString();
    252                 Log.i(TAG, "Connecting to " + mAccessPointSsid);
    253                 mStartButton.setEnabled(isReadyToStart());
    254             }
    255         });
    256         mPskEditText.addTextChangedListener(new TextWatcher() {
    257             @Override
    258             public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
    259 
    260             @Override
    261             public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
    262 
    263             @Override
    264             public void afterTextChanged(Editable editable) {
    265                 mPskValue = editable.toString();
    266                 mStartButton.setEnabled(isReadyToStart());
    267             }
    268         });
    269         mStartButton.setOnClickListener(view -> processStartClicked());
    270     }
    271 
    272     private void setupBroadcastReceivers() {
    273         IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
    274         intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    275         intentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
    276         intentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
    277         registerReceiver(mMultiNetConnectivityReceiver, intentFilter);
    278     }
    279 
    280     private void destroyBroadcastReceivers() {
    281         unregisterReceiver(mMultiNetConnectivityReceiver);
    282     }
    283 
    284     private boolean isReadyToStart() {
    285         return !(TextUtils.isEmpty(mAccessPointSsid) || TextUtils.isEmpty(mPskValue));
    286     }
    287 
    288     private static boolean isNetworkCellularAndHasInternet(ConnectivityManager connectivityManager,
    289             Network network) {
    290         NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network);
    291         return capabilities.hasTransport(TRANSPORT_CELLULAR)
    292                 && capabilities.hasCapability(NET_CAPABILITY_INTERNET);
    293     }
    294 
    295     private boolean isMobileDataEnabled(TelephonyManager telephonyManager) {
    296         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    297             return telephonyManager.isDataEnabled();
    298         }
    299         Network[] allNetworks = mConnectivityManager.getAllNetworks();
    300         for (Network network : allNetworks) {
    301             if (isNetworkCellularAndHasInternet(mConnectivityManager, network)) {
    302                 return true;
    303             }
    304         }
    305         return false;
    306     }
    307 
    308     private boolean checkPreRequisites() {
    309         TelephonyManager telephonyManager =
    310                 (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
    311         if (telephonyManager == null) {
    312             Log.e(TAG, "Device does not have telephony manager");
    313             mTestInfoView.setText(R.string.multinetwork_connectivity_test_all_prereq_1);
    314             return false;
    315         } else if (!isMobileDataEnabled(telephonyManager)) {
    316             Log.e(TAG, "Device mobile data is not available");
    317             mTestInfoView.setText(R.string.multinetwork_connectivity_test_all_prereq_2);
    318             return false;
    319         }
    320         return true;
    321     }
    322 
    323     /**
    324      * If tester went back and came in again, make sure that test resumes from the previous state.
    325      */
    326     private void setupCurrentTestStateOnResume() {
    327         mCurrentValidator = null;
    328         mStartButton.setEnabled(false);
    329 
    330         if (!checkPreRequisites()) {
    331             return;
    332         }
    333 
    334         for (int i = 0; i < mMultiNetworkValidators.length; i++) {
    335             if (mMultiNetworkValidators[i].mValidatorState != COMPLETED) {
    336                 mCurrentValidator = mMultiNetworkValidators[i];
    337                 break;
    338             }
    339         }
    340         if (mCurrentValidator != null) {
    341             mTestNameView.setText(mCurrentValidator.mTestDescription);
    342 
    343             switch (mCurrentValidator.mValidatorState) {
    344                 case NOT_STARTED:
    345                     mStartButton.setText(R.string.multinetwork_connectivity_test_start);
    346                     mStartButton.setEnabled(isReadyToStart());
    347                     break;
    348                 case STARTED:
    349                     mTestInfoView.setText(getResources().getString(
    350                             mCurrentValidator.mTestProgressMessage));
    351                     break;
    352                 case WAITING_FOR_USER_INPUT:
    353                     mStartButton.setText(R.string.multinetwork_connectivity_test_continue);
    354                     mStartButton.setEnabled(true);
    355                     mTestInfoView.setText(getResources().getString(
    356                             mCurrentValidator.mTestProgressMessage));
    357                 case COMPLETED:
    358                     break;
    359             }
    360             mTestNameView.setText(mCurrentValidator.mTestDescription);
    361         } else {
    362             // All tests completed, so need to re run. It's not likely to get here as
    363             // the default action when all test completes is to mark success and finish.
    364             mStartButton.setText(R.string.multinetwork_connectivity_test_rerun);
    365             mStartButton.setEnabled(true);
    366             rerunMultinetworkTests();
    367             mCurrentValidator = mMultiNetworkValidators[0];
    368         }
    369     }
    370 
    371     private void rerunMultinetworkTests() {
    372         for (MultiNetworkValidator validator : mMultiNetworkValidators) {
    373             validator.reset();
    374         }
    375     }
    376 
    377     private void requestUserConfirmation() {
    378         mMainHandler.post(() -> {
    379             mStartButton.setText(R.string.multinetwork_connectivity_test_continue);
    380             mStartButton.setEnabled(true);
    381         });
    382     }
    383 
    384     private void processStartClicked() {
    385         if (mCurrentValidator == null) {
    386             rerunMultinetworkTests();
    387             setupCurrentTestStateOnResume();
    388         }
    389         mStartButton.setEnabled(false);
    390         if (mCurrentValidator.mValidatorState == NOT_STARTED) {
    391             mCurrentValidator.startTest();
    392         } else if (mCurrentValidator.mValidatorState == WAITING_FOR_USER_INPUT) {
    393             mStartButton.setEnabled(false);
    394             mCurrentValidator.continueWithTest();
    395         }
    396     }
    397 
    398     private WifiConfiguration buildWifiConfiguration() {
    399         WifiConfiguration wifiConfiguration = new WifiConfiguration();
    400         wifiConfiguration.SSID = "\"" + mAccessPointSsid + "\"";
    401         wifiConfiguration.preSharedKey = "\"" + mPskValue + "\"";
    402         wifiConfiguration.status = WifiConfiguration.Status.ENABLED;
    403         wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
    404         wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
    405         wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
    406         wifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
    407         wifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
    408         wifiConfiguration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
    409         return wifiConfiguration;
    410     }
    411 
    412     private int addOrUpdateNetwork() {
    413         List<WifiConfiguration> availableConfigurations = mWifiManager.getConfiguredNetworks();
    414         for (WifiConfiguration configuration : availableConfigurations) {
    415             if (mAccessPointSsid.equals(configuration.SSID)) {
    416                 return configuration.networkId;
    417             }
    418         }
    419         int newNetwork = mWifiManager.addNetwork(buildWifiConfiguration());
    420         return newNetwork;
    421     }
    422 
    423     private boolean isConnectedToExpectedWifiNetwork() {
    424         WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
    425         DhcpInfo dhcpInfo = mWifiManager.getDhcpInfo();
    426         Log.i(TAG, "Checking connected to expected " + mAccessPointSsid);
    427         if (wifiInfo != null
    428                 && wifiInfo.getSupplicantState().equals(SupplicantState.COMPLETED)
    429                 && dhcpInfo != null) {
    430             String failsafeSsid = String.format("\"%s\"", mAccessPointSsid);
    431             Log.i(TAG, "Connected to " + wifiInfo.getSSID() + " expected " + mAccessPointSsid);
    432             return mAccessPointSsid.equals(wifiInfo.getSSID())
    433                     || failsafeSsid.equals(wifiInfo.getSSID());
    434         }
    435         return false;
    436     }
    437 
    438     private void startTimerCountdownDisplay(int timeoutInSeconds) {
    439         mMainHandler.post(() -> mSecondsToCompletion = timeoutInSeconds);
    440         mMainHandler.post(mTimeToCompletionRunnable);
    441     }
    442 
    443     private void stopTimerCountdownDisplay() {
    444         mMainHandler.removeCallbacks(mTimeToCompletionRunnable);
    445         mStartButton.setText("--");
    446     }
    447 
    448     /**
    449      * Manage the connectivity state for each MultinetworkValidation.
    450      */
    451     private class TestConnectivityState {
    452         private final MultiNetworkValidator mMultiNetworkValidator;
    453 
    454         final NetworkCallback mWifiNetworkCallback = new NetworkCallback() {
    455             @Override
    456             public void onAvailable(Network network) {
    457                 Log.i(TAG, "Wifi network available " + network.netId);
    458                 stopTimerDisplayIfRequested();
    459                 mMultiNetworkValidator.onWifiNetworkConnected(network);
    460             }
    461 
    462             @Override
    463             public void onUnavailable() {
    464                 Log.e(TAG, "Failed to connect to wifi");
    465                 stopTimerDisplayIfRequested();
    466                 mMultiNetworkValidator.onWifiNetworkUnavailable();
    467             }
    468         };
    469         final NetworkCallback mCellularNetworkCallback = new NetworkCallback() {
    470             @Override
    471             public void onAvailable(Network network) {
    472                 Log.i(TAG, "Cellular network available " + network.netId);
    473                 stopTimerDisplayIfRequested();
    474                 mMultiNetworkValidator.onCellularNetworkConnected(network);
    475             }
    476 
    477             @Override
    478             public void onUnavailable() {
    479                 Log.e(TAG, "Cellular network unavailable ");
    480                 stopTimerDisplayIfRequested();
    481                 mMultiNetworkValidator.onCellularNetworkUnavailable();
    482             }
    483         };
    484         boolean mCellularNetworkRequested;
    485         boolean mWifiNetworkRequested;
    486         boolean mTimerStartRequested;
    487 
    488         TestConnectivityState(MultiNetworkValidator validator) {
    489             mMultiNetworkValidator = validator;
    490         }
    491 
    492         void reset() {
    493             mMainHandler.post(() -> stopTimerDisplayIfRequested());
    494             if (mWifiNetworkRequested) {
    495                 mConnectivityManager.unregisterNetworkCallback(mWifiNetworkCallback);
    496                 mWifiNetworkRequested = false;
    497             }
    498             if (mCellularNetworkRequested) {
    499                 mConnectivityManager.unregisterNetworkCallback(mCellularNetworkCallback);
    500                 mCellularNetworkRequested = false;
    501             }
    502         }
    503 
    504         private void connectToWifiNetwork(boolean requireInternet) {
    505             // If device is not connected to the expected WifiNetwork, connect to the wifi Network.
    506             // Timeout with failure if it can't connect.
    507             if (!isConnectedToExpectedWifiNetwork()) {
    508                 int network = addOrUpdateNetwork();
    509                 WifiManager wifiManager = (WifiManager) getApplicationContext()
    510                         .getSystemService(Context.WIFI_SERVICE);
    511                 wifiManager.enableNetwork(network, true);
    512             }
    513             startTimerDisplay(WIFI_NETWORK_CONNECT_TIMEOUT_MS / 1000);
    514             NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder()
    515                     .addTransportType(TRANSPORT_WIFI);
    516             if (requireInternet) {
    517                 networkRequestBuilder.addCapability(NET_CAPABILITY_INTERNET);
    518             }
    519             NetworkRequest networkRequest = networkRequestBuilder.build();
    520             mWifiNetworkRequested = true;
    521             mConnectivityManager.requestNetwork(networkRequest, mWifiNetworkCallback,
    522                     mMainHandler, WIFI_NETWORK_CONNECT_TIMEOUT_MS);
    523         }
    524 
    525         private void connectToCellularNetwork() {
    526             NetworkRequest networkRequest = new NetworkRequest.Builder()
    527                     .addTransportType(TRANSPORT_CELLULAR)
    528                     .addCapability(NET_CAPABILITY_INTERNET)
    529                     .build();
    530             startTimerDisplay(CELLULAR_NETWORK_CONNECT_TIMEOUT_MS / 1000);
    531             mCellularNetworkRequested = true;
    532             mConnectivityManager.requestNetwork(networkRequest, mCellularNetworkCallback,
    533                     mMainHandler, CELLULAR_NETWORK_CONNECT_TIMEOUT_MS);
    534         }
    535 
    536         private void startTimerDisplay(int timeInSeconds) {
    537             startTimerCountdownDisplay(timeInSeconds);
    538             mTimerStartRequested = true;
    539         }
    540 
    541         /** Timer is a shared resource, change the state only if it's started in a request. */
    542         private void stopTimerDisplayIfRequested() {
    543             if (mTimerStartRequested) {
    544                 mTimerStartRequested = false;
    545                 stopTimerCountdownDisplay();
    546             }
    547         }
    548     }
    549 
    550     /**
    551      * Manage the lifecycle of each test to be run in the validator.
    552      *
    553      * Each test goes through this cycle
    554      * - Start
    555      * - Connect to cellular network
    556      * - Connect to wifi network
    557      * - Check expectation
    558      * - End test
    559      */
    560     private abstract class MultiNetworkValidator {
    561         final String mTestName;
    562         final MultinetworkTestCallback mTestCallback;
    563         final TestConnectivityState mConnectivityState;
    564 
    565         int mTestDescription;
    566         boolean mTestResult = false;
    567         ValidatorState mValidatorState;
    568         int mTestResultMessage = -1;
    569         int mTestProgressMessage;
    570 
    571         MultiNetworkValidator(MultinetworkTestCallback testCallback,
    572                 String testName, int testDescription) {
    573             mTestCallback = testCallback;
    574             mTestName = testName;
    575             mTestDescription = testDescription;
    576             mConnectivityState = new TestConnectivityState(this);
    577             mValidatorState = NOT_STARTED;
    578         }
    579 
    580         /** Start test if not started. */
    581         void startTest() {
    582             if (mValidatorState == NOT_STARTED) {
    583                 mTestCallback.testStarted();
    584                 WifiManager wifiManager = (WifiManager) getApplicationContext()
    585                         .getSystemService(Context.WIFI_SERVICE);
    586                 wifiManager.setWifiEnabled(false);
    587                 mMainHandler.postDelayed(() -> {
    588                     wifiManager.setWifiEnabled(true);
    589                     mTestCallback.testProgress(
    590                             R.string.multinetwork_connectivity_test_connect_cellular);
    591                     mConnectivityState.connectToCellularNetwork();
    592                 }, ENABLE_DISABLE_WIFI_DELAY_MS);
    593             }
    594         }
    595 
    596         /** Make sure that the state is restored for re-running the test. */
    597         void reset() {
    598             mValidatorState = NOT_STARTED;
    599             mTestResultMessage = -1;
    600             mTestProgressMessage = -1;
    601         }
    602 
    603         /** Called when user has requested to continue with the test */
    604         void continueWithTest() {
    605             mValidatorState = STARTED;
    606         }
    607 
    608 
    609         void connectToWifi() {
    610             mTestCallback.testProgress(R.string.multinetwork_connectivity_test_connect_wifi);
    611             mConnectivityState.connectToWifiNetwork(false);
    612         }
    613 
    614         void onCellularNetworkUnavailable() {
    615             endTest(false, R.string.multinetwork_status_mobile_connect_timed_out);
    616         }
    617 
    618         void endTest(boolean status, int messageResId) {
    619             Log.i(TAG, "Ending test with status " + status + " message " +
    620                 MultiNetworkConnectivityTestActivity.this.getResources().getString(messageResId));
    621             mMainHandler.post(() -> {
    622                 mTestResult = status;
    623                 mTestResultMessage = messageResId;
    624                 mValidatorState = COMPLETED;
    625                 mTestCallback.testCompleted(MultiNetworkValidator.this);
    626                 mConnectivityState.reset();
    627             });
    628         }
    629 
    630         /** Called when cellular network is connected. */
    631         void onCellularNetworkConnected(Network network) {
    632             onContinuePreWifiConnect();
    633         }
    634 
    635         /**
    636          * @param transport The active network has this transport type
    637          * @return
    638          */
    639         boolean isExpectedTransportForActiveNetwork(int transport) {
    640             Network activeNetwork = mConnectivityManager.getActiveNetwork();
    641             NetworkCapabilities activeNetworkCapabilities =
    642                     mConnectivityManager.getNetworkCapabilities(activeNetwork);
    643             Log.i(TAG, "Network capabilities for " + activeNetwork.netId + " "
    644                     + activeNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
    645             return activeNetworkCapabilities.hasTransport(transport)
    646                     && activeNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET);
    647         }
    648 
    649         /**
    650          * @param network to check if connected or not.
    651          * @return
    652          */
    653         boolean isNetworkConnected(Network network) {
    654             NetworkInfo networkInfo = mConnectivityManager.getNetworkInfo(network);
    655             boolean status = networkInfo != null && networkInfo.isConnectedOrConnecting();
    656             Log.i(TAG, "Network connection status " + network.netId + " " + status);
    657             return status;
    658         }
    659 
    660         /**
    661          * Called before connecting to wifi. Specially if the concrete validator wants to
    662          * prompt a message
    663          */
    664         abstract void onContinuePreWifiConnect();
    665 
    666         /** Called when a wifi network is connected and available */
    667         void onWifiNetworkConnected(Network network) {
    668             Log.i(TAG, "Wifi network connected " + network.netId);
    669         }
    670 
    671         void onWifiNetworkUnavailable() {
    672             endTest(false, R.string.multinetwork_status_wifi_connect_timed_out);
    673         }
    674     }
    675 
    676     /**
    677      * Test that device does not lose cellular connectivity when it's connected to an access
    678      * point with no connectivity.
    679      */
    680     private class ConnectToWifiWithNoInternetValidator extends MultiNetworkValidator {
    681 
    682         ConnectToWifiWithNoInternetValidator(int description) {
    683             super(mMultinetworkTestCallback, "no_internet_test", description);
    684         }
    685 
    686 
    687         @Override
    688         void continueWithTest() {
    689             super.continueWithTest();
    690             connectToWifi();
    691         }
    692 
    693         @Override
    694         void onContinuePreWifiConnect() {
    695             mTestProgressMessage = R.string.multinetwork_connectivity_test_1_prereq;
    696             mTestCallback.testProgress(mTestProgressMessage);
    697             mValidatorState = WAITING_FOR_USER_INPUT;
    698             requestUserConfirmation();
    699         }
    700 
    701         @Override
    702         void onWifiNetworkConnected(Network wifiNetwork) {
    703             super.onWifiNetworkConnected(wifiNetwork);
    704             if (isConnectedToExpectedWifiNetwork()) {
    705                 startTimerCountdownDisplay(CELLULAR_NETWORK_RESTORE_TIMEOUT_MS / 1000);
    706                 mTestCallback.testProgress(R.string.multinetwork_connectivity_test_progress_2);
    707 
    708                 // Wait for CELLULAR_NETWORK_RESTORE_TIMEOUT_MS, before checking if there is still
    709                 // the active network as the cell network.
    710                 mMainHandler.postDelayed(() -> {
    711                     stopTimerCountdownDisplay();
    712                     mMainHandler.post(() -> {
    713                         if (isExpectedTransportForActiveNetwork(TRANSPORT_CELLULAR)
    714                                 && isNetworkConnected(wifiNetwork)) {
    715                             Log.d(TAG, "PASS test as device has connectivity");
    716                             endTest(true, R.string.multinetwork_status_mobile_restore_success);
    717                         } else {
    718                             Log.d(TAG, "Fail test as device didn't have connectivity");
    719                             endTest(false, R.string.multinetwork_status_mobile_restore_failed);
    720                         }
    721                     });
    722                 }, CELLULAR_NETWORK_RESTORE_TIMEOUT_MS);
    723             } else {
    724                 endTest(false, R.string.multinetwork_status_wifi_connect_wrong_ap);
    725             }
    726         }
    727     }
    728 
    729     /**
    730      * Test that device restores lost cellular connectivity when it's connected to an access
    731      * point which loses internet connectivity.
    732      */
    733     private class ConnectToWifiWithIntermittentInternetValidator extends MultiNetworkValidator {
    734         boolean mWaitingForWifiConnect = false;
    735         boolean mWaitingForCelluarToConnectBack = false;
    736         Network mWifiNetwork;
    737 
    738         ConnectToWifiWithIntermittentInternetValidator(int description) {
    739             super(mMultinetworkTestCallback, "no_internet_test", description);
    740         }
    741 
    742         @Override
    743         void continueWithTest() {
    744             super.continueWithTest();
    745             if (mWaitingForWifiConnect) {
    746                 connectToWifi();
    747             } else if (mWaitingForCelluarToConnectBack) {
    748                 mWaitingForCelluarToConnectBack = false;
    749                 waitForConnectivityRestore();
    750             }
    751         }
    752 
    753         @Override
    754         void onContinuePreWifiConnect() {
    755             mTestProgressMessage = R.string.multinetwork_connectivity_test_2_prereq_1;
    756             mTestCallback.testProgress(mTestProgressMessage);
    757             mValidatorState = WAITING_FOR_USER_INPUT;
    758             mWaitingForWifiConnect = true;
    759             requestUserConfirmation();
    760         }
    761 
    762         @Override
    763         void connectToWifi() {
    764             mTestCallback.testProgress(R.string.multinetwork_connectivity_test_connect_wifi);
    765             mConnectivityState.connectToWifiNetwork(true);
    766         }
    767 
    768         @Override
    769         void onWifiNetworkConnected(Network wifiNetwork) {
    770             super.onWifiNetworkConnected(wifiNetwork);
    771             if (isConnectedToExpectedWifiNetwork()) {
    772                 // If the device is connected to the expected network, then update the wifi
    773                 // network to the latest.
    774                 mWifiNetwork = wifiNetwork;
    775                 // Do further processing only when the test is requesting and waiting for a wifi
    776                 // connection.
    777                 if (mWaitingForWifiConnect) {
    778                     mWaitingForWifiConnect = false;
    779                     startTimerCountdownDisplay(WIFI_NETWORK_CONNECT_TO_BE_ACTIVE_MS / 1000);
    780 
    781                     // Wait for WIFI_NETWORK_CONNECT_TO_BE_ACTIVE_MS, before checking
    782                     // if device has the active network as wifi network..
    783                     mTestCallback.testProgress(R.string.multinetwork_connectivity_test_progress_2);
    784                     mMainHandler.postDelayed(() -> {
    785                         stopTimerCountdownDisplay();
    786                         // In this case both active and peer are same as Wifi has internet access.
    787                         if (isExpectedTransportForActiveNetwork(TRANSPORT_WIFI)
    788                                 && isNetworkConnected(mWifiNetwork)) {
    789                             // Ask the user to turn off wifi on the router and check connectivity.
    790                             mTestProgressMessage =
    791                                     R.string.multinetwork_connectivity_test_2_prereq_2;
    792                             mValidatorState = WAITING_FOR_USER_INPUT;
    793                             mTestCallback.testProgress(mTestProgressMessage);
    794                             mWaitingForCelluarToConnectBack = true;
    795                             requestUserConfirmation();
    796                         } else {
    797                             Log.d(TAG, "Fail test as device didn't have connectivity");
    798                             endTest(false, R.string.multinetwork_status_wifi_connectivity_failed);
    799                         }
    800                     }, WIFI_NETWORK_CONNECT_TO_BE_ACTIVE_MS);
    801                 }
    802             } else {
    803                 endTest(false, R.string.multinetwork_status_wifi_connect_wrong_ap);
    804             }
    805         }
    806 
    807         @Override
    808         void reset() {
    809             super.reset();
    810             mWaitingForCelluarToConnectBack = false;
    811             mWaitingForWifiConnect = false;
    812             mWifiNetwork = null;
    813         }
    814 
    815         @Override
    816         void onWifiNetworkUnavailable() {
    817             if (mWaitingForWifiConnect) {
    818                 super.onWifiNetworkUnavailable();
    819             }
    820         }
    821 
    822         void waitForConnectivityRestore() {
    823             mTestCallback.testProgress(R.string.multinetwork_connectivity_test_progress_1);
    824             mConnectivityManager.reportNetworkConnectivity(mWifiNetwork, false);
    825             startTimerCountdownDisplay(
    826                     CELLULAR_NETWORK_RESTORE_AFTER_WIFI_INTERNET_LOST_TIMEOUT_MS / 1000);
    827             // Wait for CELLULAR_NETWORK_RESTORE_AFTER_WIFI_INTERNET_LOST_TIMEOUT_MS,
    828             // before checking if device now has the active network as cell network.
    829             mMainHandler.postDelayed(() -> {
    830                 stopTimerCountdownDisplay();
    831                 // Check if device has fallen back to cellular network when it loses internet access
    832                 // in the wifi network.
    833                 if (isExpectedTransportForActiveNetwork(TRANSPORT_CELLULAR)
    834                         && isNetworkConnected(mWifiNetwork)) {
    835                     Log.d(TAG, "PASS test as device has connectivity");
    836                     endTest(true, R.string.multinetwork_status_mobile_restore_success);
    837                 } else {
    838                     Log.d(TAG, "Fail test as device didn't have connectivity");
    839                     endTest(false, R.string.multinetwork_status_mobile_restore_failed);
    840                 }
    841             }, CELLULAR_NETWORK_RESTORE_AFTER_WIFI_INTERNET_LOST_TIMEOUT_MS);
    842         }
    843     }
    844 }
    845