• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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