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