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