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