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