From 8a14c224e30ee4a76fd83cb92c47e00bb5f4abea Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 19 Apr 2016 13:00:51 +0900 Subject: [PATCH] Add more test coverage to ConnectivityManagerTest. 1. Test NetworkCallbacks as well as CONNECTIVITY_ACTION, since we are moving away from CONNECTIVITY_ACTION. 2. Use the Network objects we get back to test Network#getSocketFactory(). 3. Check that TCP connections are closed with ECONNABORTED when a network disconnects. Bug: 28251576 Change-Id: I41a438b82ef9251e52866332f3445f1bf876e04f --- .../net/cts/ConnectivityManagerTest.java | 156 +++++++++++++++--- 1 file changed, 134 insertions(+), 22 deletions(-) diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java index 4112466877..231db97d2b 100644 --- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java +++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java @@ -38,9 +38,16 @@ import android.net.wifi.WifiManager; import android.test.AndroidTestCase; import android.util.Log; import android.os.SystemProperties; +import android.system.Os; +import android.system.OsConstants; import com.android.internal.telephony.PhoneConstants; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.Socket; +import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -57,7 +64,15 @@ public class ConnectivityManagerTest extends AndroidTestCase { public static final int TYPE_MOBILE = ConnectivityManager.TYPE_MOBILE; public static final int TYPE_WIFI = ConnectivityManager.TYPE_WIFI; + private static final int HOST_ADDRESS = 0x7f000001;// represent ip 127.0.0.1 + private static final String TEST_HOST = "connectivitycheck.gstatic.com"; + private static final int SOCKET_TIMEOUT_MS = 2000; + private static final int HTTP_PORT = 80; + private static final String HTTP_REQUEST = + "GET /generate_204 HTTP/1.0\r\n" + + "Host: " + TEST_HOST + "\r\n" + + "Connection: keep-alive\r\n\r\n"; // Action sent to ConnectivityActionReceiver when a network callback is sent via PendingIntent. private static final String NETWORK_CALLBACK_ACTION = @@ -249,6 +264,12 @@ public class ConnectivityManagerTest extends AndroidTestCase { mCm.getBackgroundDataSetting(); } + private NetworkRequest makeWifiNetworkRequest() { + return new NetworkRequest.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .build(); + } + /** * Exercises both registerNetworkCallback and unregisterNetworkCallback. This checks to * see if we get a callback for the TRANSPORT_WIFI transport type being available. @@ -265,16 +286,14 @@ public class ConnectivityManagerTest extends AndroidTestCase { } // We will register for a WIFI network being available or lost. - final NetworkRequest request = new NetworkRequest.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - .build(); final TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerNetworkCallback(request, callback); + mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback); final TestNetworkCallback defaultTrackingCallback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(defaultTrackingCallback); final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled(); + Network wifiNetwork = null; try { // Make sure WiFi is connected to an access point to start with. @@ -285,10 +304,11 @@ public class ConnectivityManagerTest extends AndroidTestCase { // Now we should expect to get a network callback about availability of the wifi // network even if it was already connected as a state-based action when the callback // is registered. - assertTrue("Did not receive NetworkCallback.onAvailable for TRANSPORT_WIFI", - callback.waitForAvailable()); + wifiNetwork = callback.waitForAvailable(); + assertNotNull("Did not receive NetworkCallback.onAvailable for TRANSPORT_WIFI", + wifiNetwork); - assertTrue("Did not receive NetworkCallback.onAvailable for any default network", + assertNotNull("Did not receive NetworkCallback.onAvailable for any default network", defaultTrackingCallback.waitForAvailable()); } catch (InterruptedException e) { fail("Broadcast receiver or NetworkCallback wait was interrupted."); @@ -298,7 +318,7 @@ public class ConnectivityManagerTest extends AndroidTestCase { // Return WiFi to its original enabled/disabled state. if (!previousWifiEnabledState) { - disconnectFromWifi(); + disconnectFromWifi(wifiNetwork); } } } @@ -329,10 +349,7 @@ public class ConnectivityManagerTest extends AndroidTestCase { mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); // We will register for a WIFI network being available or lost. - NetworkRequest request = new NetworkRequest.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - .build(); - mCm.registerNetworkCallback(request, pendingIntent); + mCm.registerNetworkCallback(makeWifiNetworkRequest(), pendingIntent); final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled(); @@ -356,7 +373,7 @@ public class ConnectivityManagerTest extends AndroidTestCase { // Return WiFi to its original enabled/disabled state. if (!previousWifiEnabledState) { - disconnectFromWifi(); + disconnectFromWifi(null); } } } @@ -370,8 +387,10 @@ public class ConnectivityManagerTest extends AndroidTestCase { // We will toggle the state of wifi to generate a connectivity change. final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled(); + if (previousWifiEnabledState) { - disconnectFromWifi(); + Network wifiNetwork = getWifiNetwork(); + disconnectFromWifi(wifiNetwork); } else { connectToWifi(); } @@ -387,12 +406,16 @@ public class ConnectivityManagerTest extends AndroidTestCase { if (previousWifiEnabledState) { connectToWifi(); } else { - disconnectFromWifi(); + disconnectFromWifi(null); } } /** Enable WiFi and wait for it to become connected to a network. */ - private void connectToWifi() { + private Network connectToWifi() { + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback); + Network wifiNetwork = null; + ConnectivityActionReceiver receiver = new ConnectivityActionReceiver( ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED); IntentFilter filter = new IntentFilter(); @@ -402,36 +425,94 @@ public class ConnectivityManagerTest extends AndroidTestCase { boolean connected = false; try { assertTrue(mWifiManager.setWifiEnabled(true)); + // Ensure we get both an onAvailable callback and a CONNECTIVITY_ACTION. + wifiNetwork = callback.waitForAvailable(); + assertNotNull(wifiNetwork); connected = receiver.waitForState(); } catch (InterruptedException ex) { fail("connectToWifi was interrupted"); } finally { + mCm.unregisterNetworkCallback(callback); mContext.unregisterReceiver(receiver); } assertTrue("Wifi must be configured to connect to an access point for this test.", connected); + return wifiNetwork; + } + + private Socket getBoundSocket(Network network, String host, int port) throws IOException { + InetSocketAddress addr = new InetSocketAddress(host, port); + Socket s = network.getSocketFactory().createSocket(); + try { + s.setSoTimeout(SOCKET_TIMEOUT_MS); + s.connect(addr, SOCKET_TIMEOUT_MS); + } catch (IOException e) { + s.close(); + throw e; + } + return s; + } + + private void testHttpRequest(Socket s) throws IOException { + OutputStream out = s.getOutputStream(); + InputStream in = s.getInputStream(); + + final byte[] requestBytes = HTTP_REQUEST.getBytes("UTF-8"); + byte[] responseBytes = new byte[4096]; + out.write(requestBytes); + in.read(responseBytes); + assertTrue(new String(responseBytes, "UTF-8").startsWith("HTTP/1.0 204 No Content\r\n")); } /** Disable WiFi and wait for it to become disconnected from the network. */ - private void disconnectFromWifi() { + private void disconnectFromWifi(Network wifiNetworkToCheck) { + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback); + Network lostWifiNetwork = null; + ConnectivityActionReceiver receiver = new ConnectivityActionReceiver( ConnectivityManager.TYPE_WIFI, NetworkInfo.State.DISCONNECTED); IntentFilter filter = new IntentFilter(); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); mContext.registerReceiver(receiver, filter); + // Assert that we can establish a TCP connection on wifi. + Socket wifiBoundSocket = null; + if (wifiNetworkToCheck != null) { + try { + wifiBoundSocket = getBoundSocket(wifiNetworkToCheck, TEST_HOST, HTTP_PORT); + testHttpRequest(wifiBoundSocket); + } catch (IOException e) { + fail("HTTP request before wifi disconnected failed with: " + e); + } + } + boolean disconnected = false; try { assertTrue(mWifiManager.setWifiEnabled(false)); + // Ensure we get both an onLost callback and a CONNECTIVITY_ACTION. + lostWifiNetwork = callback.waitForLost(); + assertNotNull(lostWifiNetwork); disconnected = receiver.waitForState(); } catch (InterruptedException ex) { fail("disconnectFromWifi was interrupted"); } finally { + mCm.unregisterNetworkCallback(callback); mContext.unregisterReceiver(receiver); } assertTrue("Wifi failed to reach DISCONNECTED state.", disconnected); + + // Check that the socket is closed when wifi disconnects. + if (wifiBoundSocket != null) { + try { + testHttpRequest(wifiBoundSocket); + fail("HTTP request should not succeed after wifi disconnects"); + } catch (IOException expected) { + assertEquals(Os.strerror(OsConstants.ECONNABORTED), expected.getMessage()); + } + } } /** @@ -498,15 +579,48 @@ public class ConnectivityManagerTest extends AndroidTestCase { */ private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback { private final CountDownLatch mAvailableLatch = new CountDownLatch(1); + private final CountDownLatch mLostLatch = new CountDownLatch(1); - public boolean waitForAvailable() throws InterruptedException { - return mAvailableLatch.await(30, TimeUnit.SECONDS); + public Network currentNetwork; + public Network lastLostNetwork; + + public Network waitForAvailable() throws InterruptedException { + return mAvailableLatch.await(30, TimeUnit.SECONDS) ? currentNetwork : null; + } + + public Network waitForLost() throws InterruptedException { + return mLostLatch.await(30, TimeUnit.SECONDS) ? lastLostNetwork : null; } @Override public void onAvailable(Network network) { + currentNetwork = network; mAvailableLatch.countDown(); } + + @Override + public void onLost(Network network) { + lastLostNetwork = network; + if (network.equals(currentNetwork)) { + currentNetwork = null; + } + mLostLatch.countDown(); + } + } + + private Network getWifiNetwork() { + TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback); + Network network = null; + try { + network = callback.waitForAvailable(); + } catch (InterruptedException e) { + fail("NetworkCallback wait was interrupted."); + } finally { + mCm.unregisterNetworkCallback(callback); + } + assertNotNull("Cannot find Network for wifi. Is wifi connected?", network); + return network; } /** Verify restricted networks cannot be requested. */ @@ -523,8 +637,6 @@ public class ConnectivityManagerTest extends AndroidTestCase { try { mCm.requestNetwork(request, callback); fail("No exception thrown when restricted network requested."); - } catch (SecurityException e) { - // Expected. - } + } catch (SecurityException expected) {} } }