1 /* 2 * Copyright (C) 2009 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 android.net.cts; 18 19 import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; 20 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 21 22 import android.app.PendingIntent; 23 import android.content.BroadcastReceiver; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.pm.PackageManager; 29 import android.net.ConnectivityManager; 30 import android.net.ConnectivityManager.NetworkCallback; 31 import android.net.Network; 32 import android.net.NetworkCapabilities; 33 import android.net.NetworkConfig; 34 import android.net.NetworkInfo; 35 import android.net.NetworkInfo.DetailedState; 36 import android.net.NetworkInfo.State; 37 import android.net.NetworkRequest; 38 import android.net.wifi.WifiManager; 39 import android.os.SystemProperties; 40 import android.system.Os; 41 import android.system.OsConstants; 42 import android.test.AndroidTestCase; 43 import android.util.Log; 44 45 import com.android.internal.telephony.PhoneConstants; 46 47 import java.io.InputStream; 48 import java.io.IOException; 49 import java.io.OutputStream; 50 import java.net.Socket; 51 import java.net.InetSocketAddress; 52 import java.util.HashMap; 53 import java.util.concurrent.CountDownLatch; 54 import java.util.concurrent.LinkedBlockingQueue; 55 import java.util.concurrent.TimeUnit; 56 57 public class ConnectivityManagerTest extends AndroidTestCase { 58 59 private static final String TAG = ConnectivityManagerTest.class.getSimpleName(); 60 61 private static final String FEATURE_ENABLE_HIPRI = "enableHIPRI"; 62 63 public static final int TYPE_MOBILE = ConnectivityManager.TYPE_MOBILE; 64 public static final int TYPE_WIFI = ConnectivityManager.TYPE_WIFI; 65 66 private static final int HOST_ADDRESS = 0x7f000001;// represent ip 127.0.0.1 67 private static final String TEST_HOST = "connectivitycheck.gstatic.com"; 68 private static final int SOCKET_TIMEOUT_MS = 2000; 69 private static final int SEND_BROADCAST_TIMEOUT = 30000; 70 private static final int HTTP_PORT = 80; 71 private static final String HTTP_REQUEST = 72 "GET /generate_204 HTTP/1.0\r\n" + 73 "Host: " + TEST_HOST + "\r\n" + 74 "Connection: keep-alive\r\n\r\n"; 75 76 // Action sent to ConnectivityActionReceiver when a network callback is sent via PendingIntent. 77 private static final String NETWORK_CALLBACK_ACTION = 78 "ConnectivityManagerTest.NetworkCallbackAction"; 79 80 // Intent string to get the number of wifi CONNECTIVITY_ACTION callbacks the test app has seen 81 public static final String GET_WIFI_CONNECTIVITY_ACTION_COUNT = 82 "android.net.cts.appForApi23.getWifiConnectivityActionCount"; 83 84 // device could have only one interface: data, wifi. 85 private static final int MIN_NUM_NETWORK_TYPES = 1; 86 87 private Context mContext; 88 private ConnectivityManager mCm; 89 private WifiManager mWifiManager; 90 private PackageManager mPackageManager; 91 private final HashMap<Integer, NetworkConfig> mNetworks = 92 new HashMap<Integer, NetworkConfig>(); 93 94 @Override setUp()95 protected void setUp() throws Exception { 96 super.setUp(); 97 mContext = getContext(); 98 mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); 99 mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 100 mPackageManager = mContext.getPackageManager(); 101 102 // Get com.android.internal.R.array.networkAttributes 103 int resId = mContext.getResources().getIdentifier("networkAttributes", "array", "android"); 104 String[] naStrings = mContext.getResources().getStringArray(resId); 105 //TODO: What is the "correct" way to determine if this is a wifi only device? 106 boolean wifiOnly = SystemProperties.getBoolean("ro.radio.noril", false); 107 for (String naString : naStrings) { 108 try { 109 NetworkConfig n = new NetworkConfig(naString); 110 if (wifiOnly && ConnectivityManager.isNetworkTypeMobile(n.type)) { 111 continue; 112 } 113 mNetworks.put(n.type, n); 114 } catch (Exception e) {} 115 } 116 } 117 testIsNetworkTypeValid()118 public void testIsNetworkTypeValid() { 119 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE)); 120 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_WIFI)); 121 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_MMS)); 122 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_SUPL)); 123 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_DUN)); 124 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_HIPRI)); 125 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_WIMAX)); 126 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_BLUETOOTH)); 127 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_DUMMY)); 128 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_ETHERNET)); 129 assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_FOTA)); 130 assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_IMS)); 131 assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_CBS)); 132 assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_WIFI_P2P)); 133 assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_IA)); 134 assertFalse(mCm.isNetworkTypeValid(-1)); 135 assertTrue(mCm.isNetworkTypeValid(0)); 136 assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.MAX_NETWORK_TYPE)); 137 assertFalse(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.MAX_NETWORK_TYPE+1)); 138 139 NetworkInfo[] ni = mCm.getAllNetworkInfo(); 140 141 for (NetworkInfo n: ni) { 142 assertTrue(ConnectivityManager.isNetworkTypeValid(n.getType())); 143 } 144 145 } 146 testSetNetworkPreference()147 public void testSetNetworkPreference() { 148 // getNetworkPreference() and setNetworkPreference() are both deprecated so they do 149 // not preform any action. Verify they are at least still callable. 150 mCm.setNetworkPreference(mCm.getNetworkPreference()); 151 } 152 testGetActiveNetworkInfo()153 public void testGetActiveNetworkInfo() { 154 NetworkInfo ni = mCm.getActiveNetworkInfo(); 155 156 assertNotNull("You must have an active network connection to complete CTS", ni); 157 assertTrue(ConnectivityManager.isNetworkTypeValid(ni.getType())); 158 assertTrue(ni.getState() == State.CONNECTED); 159 } 160 testGetActiveNetwork()161 public void testGetActiveNetwork() { 162 Network network = mCm.getActiveNetwork(); 163 assertNotNull("You must have an active network connection to complete CTS", network); 164 165 NetworkInfo ni = mCm.getNetworkInfo(network); 166 assertNotNull("Network returned from getActiveNetwork was invalid", ni); 167 168 // Similar to testGetActiveNetworkInfo above. 169 assertTrue(ConnectivityManager.isNetworkTypeValid(ni.getType())); 170 assertTrue(ni.getState() == State.CONNECTED); 171 } 172 testGetNetworkInfo()173 public void testGetNetworkInfo() { 174 for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE+1; type++) { 175 if (isSupported(type)) { 176 NetworkInfo ni = mCm.getNetworkInfo(type); 177 assertTrue("Info shouldn't be null for " + type, ni != null); 178 State state = ni.getState(); 179 assertTrue("Bad state for " + type, State.UNKNOWN.ordinal() >= state.ordinal() 180 && state.ordinal() >= State.CONNECTING.ordinal()); 181 DetailedState ds = ni.getDetailedState(); 182 assertTrue("Bad detailed state for " + type, 183 DetailedState.FAILED.ordinal() >= ds.ordinal() 184 && ds.ordinal() >= DetailedState.IDLE.ordinal()); 185 } else { 186 assertNull("Info should be null for " + type, mCm.getNetworkInfo(type)); 187 } 188 } 189 } 190 testGetAllNetworkInfo()191 public void testGetAllNetworkInfo() { 192 NetworkInfo[] ni = mCm.getAllNetworkInfo(); 193 assertTrue(ni.length >= MIN_NUM_NETWORK_TYPES); 194 for (int type = 0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) { 195 int desiredFoundCount = (isSupported(type) ? 1 : 0); 196 int foundCount = 0; 197 for (NetworkInfo i : ni) { 198 if (i.getType() == type) foundCount++; 199 } 200 if (foundCount != desiredFoundCount) { 201 Log.e(TAG, "failure in testGetAllNetworkInfo. Dump of returned NetworkInfos:"); 202 for (NetworkInfo networkInfo : ni) Log.e(TAG, " " + networkInfo); 203 } 204 assertTrue("Unexpected foundCount of " + foundCount + " for type " + type, 205 foundCount == desiredFoundCount); 206 } 207 } 208 assertStartUsingNetworkFeatureUnsupported(int networkType, String feature)209 private void assertStartUsingNetworkFeatureUnsupported(int networkType, String feature) { 210 try { 211 mCm.startUsingNetworkFeature(networkType, feature); 212 fail("startUsingNetworkFeature is no longer supported in the current API version"); 213 } catch (UnsupportedOperationException expected) {} 214 } 215 assertStopUsingNetworkFeatureUnsupported(int networkType, String feature)216 private void assertStopUsingNetworkFeatureUnsupported(int networkType, String feature) { 217 try { 218 mCm.startUsingNetworkFeature(networkType, feature); 219 fail("stopUsingNetworkFeature is no longer supported in the current API version"); 220 } catch (UnsupportedOperationException expected) {} 221 } 222 assertRequestRouteToHostUnsupported(int networkType, int hostAddress)223 private void assertRequestRouteToHostUnsupported(int networkType, int hostAddress) { 224 try { 225 mCm.requestRouteToHost(networkType, hostAddress); 226 fail("requestRouteToHost is no longer supported in the current API version"); 227 } catch (UnsupportedOperationException expected) {} 228 } 229 testStartUsingNetworkFeature()230 public void testStartUsingNetworkFeature() { 231 232 final String invalidateFeature = "invalidateFeature"; 233 final String mmsFeature = "enableMMS"; 234 final int failureCode = -1; 235 final int wifiOnlyStartFailureCode = PhoneConstants.APN_REQUEST_FAILED; 236 final int wifiOnlyStopFailureCode = -1; 237 238 assertStartUsingNetworkFeatureUnsupported(TYPE_MOBILE, invalidateFeature); 239 assertStopUsingNetworkFeatureUnsupported(TYPE_MOBILE, invalidateFeature); 240 assertStartUsingNetworkFeatureUnsupported(TYPE_WIFI, mmsFeature); 241 } 242 isSupported(int networkType)243 private boolean isSupported(int networkType) { 244 // Change-Id I02eb5f22737720095f646f8db5c87fd66da129d6 added VPN support 245 // to all devices directly in software, independent of any external 246 // configuration. 247 return mNetworks.containsKey(networkType) || 248 (networkType == ConnectivityManager.TYPE_VPN); 249 } 250 testIsNetworkSupported()251 public void testIsNetworkSupported() { 252 for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) { 253 boolean supported = mCm.isNetworkSupported(type); 254 if (isSupported(type)) { 255 assertTrue(supported); 256 } else { 257 assertFalse(supported); 258 } 259 } 260 } 261 testRequestRouteToHost()262 public void testRequestRouteToHost() { 263 for (int type = -1 ; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) { 264 assertRequestRouteToHostUnsupported(type, HOST_ADDRESS); 265 } 266 } 267 testTest()268 public void testTest() { 269 mCm.getBackgroundDataSetting(); 270 } 271 makeWifiNetworkRequest()272 private NetworkRequest makeWifiNetworkRequest() { 273 return new NetworkRequest.Builder() 274 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) 275 .build(); 276 } 277 278 /** 279 * Exercises both registerNetworkCallback and unregisterNetworkCallback. This checks to 280 * see if we get a callback for the TRANSPORT_WIFI transport type being available. 281 * 282 * <p>In order to test that a NetworkCallback occurs, we need some change in the network 283 * state (either a transport or capability is now available). The most straightforward is 284 * WiFi. We could add a version that uses the telephony data connection but it's not clear 285 * that it would increase test coverage by much (how many devices have 3G radio but not Wifi?). 286 */ testRegisterNetworkCallback()287 public void testRegisterNetworkCallback() { 288 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) { 289 Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi"); 290 return; 291 } 292 293 // We will register for a WIFI network being available or lost. 294 final TestNetworkCallback callback = new TestNetworkCallback(); 295 mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback); 296 297 final TestNetworkCallback defaultTrackingCallback = new TestNetworkCallback(); 298 mCm.registerDefaultNetworkCallback(defaultTrackingCallback); 299 300 final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled(); 301 Network wifiNetwork = null; 302 303 try { 304 // Make sure WiFi is connected to an access point to start with. 305 if (!previousWifiEnabledState) { 306 connectToWifi(); 307 } 308 309 // Now we should expect to get a network callback about availability of the wifi 310 // network even if it was already connected as a state-based action when the callback 311 // is registered. 312 wifiNetwork = callback.waitForAvailable(); 313 assertNotNull("Did not receive NetworkCallback.onAvailable for TRANSPORT_WIFI", 314 wifiNetwork); 315 316 assertNotNull("Did not receive NetworkCallback.onAvailable for any default network", 317 defaultTrackingCallback.waitForAvailable()); 318 } catch (InterruptedException e) { 319 fail("Broadcast receiver or NetworkCallback wait was interrupted."); 320 } finally { 321 mCm.unregisterNetworkCallback(callback); 322 mCm.unregisterNetworkCallback(defaultTrackingCallback); 323 324 // Return WiFi to its original enabled/disabled state. 325 if (!previousWifiEnabledState) { 326 disconnectFromWifi(wifiNetwork); 327 } 328 } 329 } 330 331 /** 332 * Tests both registerNetworkCallback and unregisterNetworkCallback similarly to 333 * {@link #testRegisterNetworkCallback} except that a {@code PendingIntent} is used instead 334 * of a {@code NetworkCallback}. 335 */ testRegisterNetworkCallback_withPendingIntent()336 public void testRegisterNetworkCallback_withPendingIntent() { 337 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) { 338 Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi"); 339 return; 340 } 341 342 // Create a ConnectivityActionReceiver that has an IntentFilter for our locally defined 343 // action, NETWORK_CALLBACK_ACTION. 344 IntentFilter filter = new IntentFilter(); 345 filter.addAction(NETWORK_CALLBACK_ACTION); 346 347 ConnectivityActionReceiver receiver = new ConnectivityActionReceiver( 348 ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED); 349 mContext.registerReceiver(receiver, filter); 350 351 // Create a broadcast PendingIntent for NETWORK_CALLBACK_ACTION. 352 Intent intent = new Intent(NETWORK_CALLBACK_ACTION); 353 PendingIntent pendingIntent = PendingIntent.getBroadcast( 354 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); 355 356 // We will register for a WIFI network being available or lost. 357 mCm.registerNetworkCallback(makeWifiNetworkRequest(), pendingIntent); 358 359 final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled(); 360 361 try { 362 // Make sure WiFi is connected to an access point to start with. 363 if (!previousWifiEnabledState) { 364 connectToWifi(); 365 } 366 367 // Now we expect to get the Intent delivered notifying of the availability of the wifi 368 // network even if it was already connected as a state-based action when the callback 369 // is registered. 370 assertTrue("Did not receive expected Intent " + intent + " for TRANSPORT_WIFI", 371 receiver.waitForState()); 372 } catch (InterruptedException e) { 373 fail("Broadcast receiver or NetworkCallback wait was interrupted."); 374 } finally { 375 mCm.unregisterNetworkCallback(pendingIntent); 376 pendingIntent.cancel(); 377 mContext.unregisterReceiver(receiver); 378 379 // Return WiFi to its original enabled/disabled state. 380 if (!previousWifiEnabledState) { 381 disconnectFromWifi(null); 382 } 383 } 384 } 385 386 /** 387 * Tests reporting of connectivity changed. 388 */ testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent()389 public void testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent() { 390 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) { 391 Log.i(TAG, "testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent cannot execute unless device supports WiFi"); 392 return; 393 } 394 ConnectivityReceiver.prepare(); 395 396 toggleWifi(); 397 398 // The connectivity broadcast has been sent; push through a terminal broadcast 399 // to wait for in the receive to confirm it didn't see the connectivity change. 400 Intent finalIntent = new Intent(ConnectivityReceiver.FINAL_ACTION); 401 finalIntent.setClass(mContext, ConnectivityReceiver.class); 402 mContext.sendBroadcast(finalIntent); 403 assertFalse(ConnectivityReceiver.waitForBroadcast()); 404 } 405 testConnectivityChanged_whenRegistered_shouldReceiveIntent()406 public void testConnectivityChanged_whenRegistered_shouldReceiveIntent() { 407 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) { 408 Log.i(TAG, "testConnectivityChanged_whenRegistered_shouldReceiveIntent cannot execute unless device supports WiFi"); 409 return; 410 } 411 ConnectivityReceiver.prepare(); 412 ConnectivityReceiver receiver = new ConnectivityReceiver(); 413 IntentFilter filter = new IntentFilter(); 414 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); 415 mContext.registerReceiver(receiver, filter); 416 417 toggleWifi(); 418 Intent finalIntent = new Intent(ConnectivityReceiver.FINAL_ACTION); 419 finalIntent.setClass(mContext, ConnectivityReceiver.class); 420 mContext.sendBroadcast(finalIntent); 421 422 assertTrue(ConnectivityReceiver.waitForBroadcast()); 423 } 424 testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent()425 public void testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent() 426 throws InterruptedException { 427 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) { 428 Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi"); 429 return; 430 } 431 Intent startIntent = new Intent(); 432 startIntent.setComponent(new ComponentName("android.net.cts.appForApi23", 433 "android.net.cts.appForApi23.ConnectivityListeningActivity")); 434 mContext.startActivity(startIntent); 435 436 toggleWifi(); 437 438 Intent getConnectivityCount = new Intent(GET_WIFI_CONNECTIVITY_ACTION_COUNT); 439 assertEquals(2, sendOrderedBroadcastAndReturnResultCode( 440 getConnectivityCount, SEND_BROADCAST_TIMEOUT)); 441 } 442 sendOrderedBroadcastAndReturnResultCode( Intent intent, int timeoutMs)443 private int sendOrderedBroadcastAndReturnResultCode( 444 Intent intent, int timeoutMs) throws InterruptedException { 445 final LinkedBlockingQueue<Integer> result = new LinkedBlockingQueue<>(1); 446 mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() { 447 @Override 448 public void onReceive(Context context, Intent intent) { 449 result.offer(getResultCode()); 450 } 451 }, null, 0, null, null); 452 453 Integer resultCode = result.poll(timeoutMs, TimeUnit.MILLISECONDS); 454 assertNotNull("Timed out (more than " + timeoutMs + 455 " milliseconds) waiting for result code for broadcast", resultCode); 456 return resultCode; 457 } 458 459 // Toggle WiFi twice, leaving it in the state it started in toggleWifi()460 private void toggleWifi() { 461 if (mWifiManager.isWifiEnabled()) { 462 Network wifiNetwork = getWifiNetwork(); 463 disconnectFromWifi(wifiNetwork); 464 connectToWifi(); 465 } else { 466 connectToWifi(); 467 Network wifiNetwork = getWifiNetwork(); 468 disconnectFromWifi(wifiNetwork); 469 } 470 } 471 472 /** Enable WiFi and wait for it to become connected to a network. */ connectToWifi()473 private Network connectToWifi() { 474 final TestNetworkCallback callback = new TestNetworkCallback(); 475 mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback); 476 Network wifiNetwork = null; 477 478 ConnectivityActionReceiver receiver = new ConnectivityActionReceiver( 479 ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED); 480 IntentFilter filter = new IntentFilter(); 481 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); 482 mContext.registerReceiver(receiver, filter); 483 484 boolean connected = false; 485 try { 486 assertTrue(mWifiManager.setWifiEnabled(true)); 487 // Ensure we get both an onAvailable callback and a CONNECTIVITY_ACTION. 488 wifiNetwork = callback.waitForAvailable(); 489 assertNotNull(wifiNetwork); 490 connected = receiver.waitForState(); 491 } catch (InterruptedException ex) { 492 fail("connectToWifi was interrupted"); 493 } finally { 494 mCm.unregisterNetworkCallback(callback); 495 mContext.unregisterReceiver(receiver); 496 } 497 498 assertTrue("Wifi must be configured to connect to an access point for this test.", 499 connected); 500 return wifiNetwork; 501 } 502 getBoundSocket(Network network, String host, int port)503 private Socket getBoundSocket(Network network, String host, int port) throws IOException { 504 InetSocketAddress addr = new InetSocketAddress(host, port); 505 Socket s = network.getSocketFactory().createSocket(); 506 try { 507 s.setSoTimeout(SOCKET_TIMEOUT_MS); 508 s.connect(addr, SOCKET_TIMEOUT_MS); 509 } catch (IOException e) { 510 s.close(); 511 throw e; 512 } 513 return s; 514 } 515 testHttpRequest(Socket s)516 private void testHttpRequest(Socket s) throws IOException { 517 OutputStream out = s.getOutputStream(); 518 InputStream in = s.getInputStream(); 519 520 final byte[] requestBytes = HTTP_REQUEST.getBytes("UTF-8"); 521 byte[] responseBytes = new byte[4096]; 522 out.write(requestBytes); 523 in.read(responseBytes); 524 assertTrue(new String(responseBytes, "UTF-8").startsWith("HTTP/1.0 204 No Content\r\n")); 525 } 526 527 /** Disable WiFi and wait for it to become disconnected from the network. */ disconnectFromWifi(Network wifiNetworkToCheck)528 private void disconnectFromWifi(Network wifiNetworkToCheck) { 529 final TestNetworkCallback callback = new TestNetworkCallback(); 530 mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback); 531 Network lostWifiNetwork = null; 532 533 ConnectivityActionReceiver receiver = new ConnectivityActionReceiver( 534 ConnectivityManager.TYPE_WIFI, NetworkInfo.State.DISCONNECTED); 535 IntentFilter filter = new IntentFilter(); 536 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); 537 mContext.registerReceiver(receiver, filter); 538 539 // Assert that we can establish a TCP connection on wifi. 540 Socket wifiBoundSocket = null; 541 if (wifiNetworkToCheck != null) { 542 try { 543 wifiBoundSocket = getBoundSocket(wifiNetworkToCheck, TEST_HOST, HTTP_PORT); 544 testHttpRequest(wifiBoundSocket); 545 } catch (IOException e) { 546 fail("HTTP request before wifi disconnected failed with: " + e); 547 } 548 } 549 550 boolean disconnected = false; 551 try { 552 assertTrue(mWifiManager.setWifiEnabled(false)); 553 // Ensure we get both an onLost callback and a CONNECTIVITY_ACTION. 554 lostWifiNetwork = callback.waitForLost(); 555 assertNotNull(lostWifiNetwork); 556 disconnected = receiver.waitForState(); 557 } catch (InterruptedException ex) { 558 fail("disconnectFromWifi was interrupted"); 559 } finally { 560 mCm.unregisterNetworkCallback(callback); 561 mContext.unregisterReceiver(receiver); 562 } 563 564 assertTrue("Wifi failed to reach DISCONNECTED state.", disconnected); 565 566 // Check that the socket is closed when wifi disconnects. 567 if (wifiBoundSocket != null) { 568 try { 569 testHttpRequest(wifiBoundSocket); 570 fail("HTTP request should not succeed after wifi disconnects"); 571 } catch (IOException expected) { 572 assertEquals(Os.strerror(OsConstants.ECONNABORTED), expected.getMessage()); 573 } 574 } 575 } 576 577 /** 578 * Receiver that captures the last connectivity change's network type and state. Recognizes 579 * both {@code CONNECTIVITY_ACTION} and {@code NETWORK_CALLBACK_ACTION} intents. 580 */ 581 private class ConnectivityActionReceiver extends BroadcastReceiver { 582 583 private final CountDownLatch mReceiveLatch = new CountDownLatch(1); 584 585 private final int mNetworkType; 586 private final NetworkInfo.State mNetState; 587 ConnectivityActionReceiver(int networkType, NetworkInfo.State netState)588 ConnectivityActionReceiver(int networkType, NetworkInfo.State netState) { 589 mNetworkType = networkType; 590 mNetState = netState; 591 } 592 onReceive(Context context, Intent intent)593 public void onReceive(Context context, Intent intent) { 594 String action = intent.getAction(); 595 NetworkInfo networkInfo = null; 596 597 // When receiving ConnectivityManager.CONNECTIVITY_ACTION, the NetworkInfo parcelable 598 // is stored in EXTRA_NETWORK_INFO. With a NETWORK_CALLBACK_ACTION, the Network is 599 // sent in EXTRA_NETWORK and we need to ask the ConnectivityManager for the NetworkInfo. 600 if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) { 601 networkInfo = intent.getExtras() 602 .getParcelable(ConnectivityManager.EXTRA_NETWORK_INFO); 603 assertNotNull("ConnectivityActionReceiver expected EXTRA_NETWORK_INFO", networkInfo); 604 } else if (NETWORK_CALLBACK_ACTION.equals(action)) { 605 Network network = intent.getExtras() 606 .getParcelable(ConnectivityManager.EXTRA_NETWORK); 607 assertNotNull("ConnectivityActionReceiver expected EXTRA_NETWORK", network); 608 networkInfo = mCm.getNetworkInfo(network); 609 if (networkInfo == null) { 610 // When disconnecting, it seems like we get an intent sent with an invalid 611 // Network; that is, by the time we call ConnectivityManager.getNetworkInfo(), 612 // it is invalid. Ignore these. 613 Log.i(TAG, "ConnectivityActionReceiver NETWORK_CALLBACK_ACTION ignoring " 614 + "invalid network"); 615 return; 616 } 617 } else { 618 fail("ConnectivityActionReceiver received unxpected intent action: " + action); 619 } 620 621 assertNotNull("ConnectivityActionReceiver didn't find NetworkInfo", networkInfo); 622 int networkType = networkInfo.getType(); 623 State networkState = networkInfo.getState(); 624 Log.i(TAG, "Network type: " + networkType + " state: " + networkState); 625 if (networkType == mNetworkType && networkInfo.getState() == mNetState) { 626 mReceiveLatch.countDown(); 627 } 628 } 629 waitForState()630 public boolean waitForState() throws InterruptedException { 631 return mReceiveLatch.await(30, TimeUnit.SECONDS); 632 } 633 } 634 635 /** 636 * Callback used in testRegisterNetworkCallback that allows caller to block on 637 * {@code onAvailable}. 638 */ 639 private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback { 640 private final CountDownLatch mAvailableLatch = new CountDownLatch(1); 641 private final CountDownLatch mLostLatch = new CountDownLatch(1); 642 643 public Network currentNetwork; 644 public Network lastLostNetwork; 645 waitForAvailable()646 public Network waitForAvailable() throws InterruptedException { 647 return mAvailableLatch.await(30, TimeUnit.SECONDS) ? currentNetwork : null; 648 } 649 waitForLost()650 public Network waitForLost() throws InterruptedException { 651 return mLostLatch.await(30, TimeUnit.SECONDS) ? lastLostNetwork : null; 652 } 653 654 @Override onAvailable(Network network)655 public void onAvailable(Network network) { 656 currentNetwork = network; 657 mAvailableLatch.countDown(); 658 } 659 660 @Override onLost(Network network)661 public void onLost(Network network) { 662 lastLostNetwork = network; 663 if (network.equals(currentNetwork)) { 664 currentNetwork = null; 665 } 666 mLostLatch.countDown(); 667 } 668 } 669 getWifiNetwork()670 private Network getWifiNetwork() { 671 TestNetworkCallback callback = new TestNetworkCallback(); 672 mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback); 673 Network network = null; 674 try { 675 network = callback.waitForAvailable(); 676 } catch (InterruptedException e) { 677 fail("NetworkCallback wait was interrupted."); 678 } finally { 679 mCm.unregisterNetworkCallback(callback); 680 } 681 assertNotNull("Cannot find Network for wifi. Is wifi connected?", network); 682 return network; 683 } 684 685 /** Verify restricted networks cannot be requested. */ testRestrictedNetworks()686 public void testRestrictedNetworks() { 687 // Verify we can request unrestricted networks: 688 NetworkRequest request = new NetworkRequest.Builder() 689 .addCapability(NET_CAPABILITY_INTERNET).build(); 690 NetworkCallback callback = new NetworkCallback(); 691 mCm.requestNetwork(request, callback); 692 mCm.unregisterNetworkCallback(callback); 693 // Verify we cannot request restricted networks: 694 request = new NetworkRequest.Builder().addCapability(NET_CAPABILITY_IMS).build(); 695 callback = new NetworkCallback(); 696 try { 697 mCm.requestNetwork(request, callback); 698 fail("No exception thrown when restricted network requested."); 699 } catch (SecurityException expected) {} 700 } 701 } 702