1 /*
2  * Copyright (C) 2015 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.legacy.api22;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.pm.PackageManager;
24 import android.net.ConnectivityManager;
25 import android.net.LinkAddress;
26 import android.net.LinkProperties;
27 import android.net.Network;
28 import android.net.NetworkInfo;
29 import android.net.wifi.WifiManager;
30 import android.os.ConditionVariable;
31 import android.test.AndroidTestCase;
32 import android.util.Log;
33 
34 import java.net.DatagramSocket;
35 import java.net.Inet4Address;
36 import java.net.InetAddress;
37 import java.util.ArrayList;
38 import java.util.List;
39 
40 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
41 import static android.net.ConnectivityManager.TYPE_MOBILE;
42 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
43 import static android.net.ConnectivityManager.TYPE_VPN;
44 import static android.net.ConnectivityManager.TYPE_WIFI;
45 
46 public class ConnectivityManagerLegacyTest extends AndroidTestCase {
47     private static final String TAG = ConnectivityManagerLegacyTest.class.getSimpleName();
48     private static final String FEATURE_ENABLE_HIPRI = "enableHIPRI";
49     private static final String HOST_ADDRESS1 = "192.0.2.1";
50     private static final String HOST_ADDRESS2 = "192.0.2.2";
51     private static final String HOST_ADDRESS3 = "192.0.2.3";
52 
53     // These are correct as of API level 22, which is what we target here.
54     private static final int APN_REQUEST_FAILED = 3;
55     private static final int MAX_NETWORK_TYPE = TYPE_VPN;
56 
57     private ConnectivityManager mCm;
58     private WifiManager mWifiManager;
59     private PackageManager mPackageManager;
60 
61     private final List<Integer>mProtectedNetworks = new ArrayList<Integer>();
62 
setUp()63     protected void setUp() throws Exception {
64         super.setUp();
65         mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
66         mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
67         mPackageManager = getContext().getPackageManager();
68 
69         // Get com.android.internal.R.array.config_protectedNetworks
70         int resId = getContext().getResources().getIdentifier("config_protectedNetworks", "array", "android");
71         int[] protectedNetworks = getContext().getResources().getIntArray(resId);
72         for (int p : protectedNetworks) {
73             mProtectedNetworks.add(p);
74         }
75     }
76 
77     // true if only the system can turn it on
isNetworkProtected(int networkType)78     private boolean isNetworkProtected(int networkType) {
79         return mProtectedNetworks.contains(networkType);
80     }
81 
ipv4AddrToInt(String addrString)82     private int ipv4AddrToInt(String addrString) throws Exception {
83         byte[] addr = ((Inet4Address) InetAddress.getByName(addrString)).getAddress();
84         return ((addr[3] & 0xff) << 24) | ((addr[2] & 0xff) << 16) |
85                 ((addr[1] & 0xff) << 8) | (addr[0] & 0xff);
86     }
87 
checkSourceAddress(String addrString, int type)88     private void checkSourceAddress(String addrString, int type) throws Exception {
89         DatagramSocket d = new DatagramSocket();
90         d.connect(InetAddress.getByName(addrString), 7);
91         InetAddress localAddress = d.getLocalAddress();
92 
93         Network[] networks = mCm.getAllNetworks();
94         for (int i = 0; i < networks.length; i++) {
95             NetworkInfo ni = mCm.getNetworkInfo(networks[i]);
96             if (ni != null && ni.getType() == type) {
97                 LinkProperties lp = mCm.getLinkProperties(networks[i]);
98                 for (LinkAddress address : lp.getLinkAddresses()) {
99                     if (address.getAddress().equals(localAddress)) {
100                         return;
101                     }
102                 }
103             }
104         }
105         fail("Local address " + localAddress + " not assigned to any network of type " + type);
106     }
107 
108     /** Test that hipri can be brought up when Wifi is enabled. */
testStartUsingNetworkFeature_enableHipri()109     public void testStartUsingNetworkFeature_enableHipri() throws Exception {
110         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
111                 || !mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
112             // This test requires a mobile data connection and WiFi.
113             return;
114         }
115 
116         // Make sure WiFi is connected to an access point.
117         connectToWifi();
118 
119         expectNetworkBroadcast(TYPE_MOBILE_HIPRI, NetworkInfo.State.CONNECTED,
120                 new Runnable() {
121                     public void run() {
122                         int ret = mCm.startUsingNetworkFeature(TYPE_MOBILE, FEATURE_ENABLE_HIPRI);
123                         assertTrue("Couldn't start using the HIPRI feature.", ret != -1);
124                     }
125                 });
126 
127         assertTrue("Couldn't requestRouteToHost using HIPRI.",
128                 mCm.requestRouteToHost(TYPE_MOBILE_HIPRI, ipv4AddrToInt(HOST_ADDRESS1)));
129 
130         try { Thread.sleep(1000); } catch(Exception e) {}
131         checkSourceAddress(HOST_ADDRESS1, TYPE_MOBILE);
132         checkSourceAddress(HOST_ADDRESS2, TYPE_WIFI);
133 
134         // TODO check dns selection
135 
136         expectNetworkBroadcast(TYPE_MOBILE_HIPRI, NetworkInfo.State.DISCONNECTED,
137                 new Runnable() {
138                     public void run() {
139                         int ret = mCm.stopUsingNetworkFeature(TYPE_MOBILE, FEATURE_ENABLE_HIPRI);
140                         assertTrue("Couldn't stop using the HIPRI feature.", ret != -1);
141                     }
142                 });
143 
144 
145         // TODO check dns selection
146         disconnectFromWifi();
147     }
148 
testStartUsingNetworkFeature()149     public void testStartUsingNetworkFeature() {
150 
151         final String invalidFeature = "invalidFeature";
152         final String mmsFeature = "enableMMS";
153         final int failureCode = -1;
154         final int wifiOnlyStartFailureCode = APN_REQUEST_FAILED;
155         final int wifiOnlyStopFailureCode = -1;
156 
157         NetworkInfo ni = mCm.getNetworkInfo(TYPE_MOBILE);
158         if (ni != null) {
159             assertEquals(APN_REQUEST_FAILED,
160                     mCm.startUsingNetworkFeature(TYPE_MOBILE, invalidFeature));
161             assertEquals(failureCode, mCm.stopUsingNetworkFeature(TYPE_MOBILE, invalidFeature));
162         } else {
163             assertEquals(wifiOnlyStartFailureCode, mCm.startUsingNetworkFeature(TYPE_MOBILE,
164                     invalidFeature));
165             assertEquals(wifiOnlyStopFailureCode, mCm.stopUsingNetworkFeature(TYPE_MOBILE,
166                     invalidFeature));
167         }
168 
169         ni = mCm.getNetworkInfo(TYPE_WIFI);
170         if (ni != null) {
171             // Should return failure because MMS is not supported on WIFI.
172             assertEquals(APN_REQUEST_FAILED, mCm.startUsingNetworkFeature(TYPE_WIFI,
173                     mmsFeature));
174             assertEquals(failureCode, mCm.stopUsingNetworkFeature(TYPE_WIFI,
175                     mmsFeature));
176         }
177     }
178 
expectNetworkBroadcast(final int type, final NetworkInfo.State state, Runnable afterWhat)179     private void expectNetworkBroadcast(final int type, final NetworkInfo.State state,
180             Runnable afterWhat) {
181         final int TIMEOUT_MS = 30 * 1000;
182         final ConditionVariable var = new ConditionVariable();
183 
184         Log.d(TAG, "Waiting for " + state + " broadcast for type " + type);
185         BroadcastReceiver receiver = new BroadcastReceiver() {
186             public void onReceive(Context context, Intent intent) {
187                 NetworkInfo ni = intent.getExtras()
188                         .getParcelable(ConnectivityManager.EXTRA_NETWORK_INFO);
189                 assertNotNull("CONNECTIVITY_ACTION with null EXTRA_NETWORK_INFO", ni);
190                 if (ni.getType() == type && ni.getState().equals(state)) {
191                     Log.d(TAG, "Received expected " + state + " broadcast for type " + type);
192                     var.open();
193                 }
194             }
195         };
196         IntentFilter filter = new IntentFilter();
197         filter.addAction(CONNECTIVITY_ACTION);
198         mContext.registerReceiver(receiver, filter);
199 
200         try {
201             afterWhat.run();
202             final String msg = "Did not receive expected " + state + " broadcast for type " + type +
203                     " after " + TIMEOUT_MS + " ms";
204             assertTrue(msg, var.block(TIMEOUT_MS));
205         } finally {
206             mContext.unregisterReceiver(receiver);
207         }
208     }
209 
isWifiConnected()210     private boolean isWifiConnected() {
211         NetworkInfo ni = mCm.getNetworkInfo(TYPE_WIFI);
212         return ni != null && ni.isConnected();
213     }
214 
setWifiState(final boolean enabled)215     private void setWifiState(final boolean enabled) {
216         if (enabled != isWifiConnected()) {
217             final NetworkInfo.State desiredState = enabled ?
218                     NetworkInfo.State.CONNECTED :
219                     NetworkInfo.State.DISCONNECTED;
220             expectNetworkBroadcast(TYPE_WIFI, desiredState, new Runnable() {
221                 public void run() {
222                     mWifiManager.setWifiEnabled(enabled);
223                 }
224             });
225         }
226     }
227 
connectToWifi()228     private void connectToWifi() {
229         setWifiState(true);
230     }
231 
disconnectFromWifi()232     private void disconnectFromWifi() {
233         setWifiState(false);
234     }
235 
isNetworkSupported(int networkType)236     private boolean isNetworkSupported(int networkType) {
237         return mCm.getNetworkInfo(networkType) != null;
238     }
239 
testRequestRouteToHost()240     public void testRequestRouteToHost() throws Exception {
241         for (int type = -1 ; type <= MAX_NETWORK_TYPE; type++) {
242             NetworkInfo ni = mCm.getNetworkInfo(type);
243             boolean expectToWork = isNetworkSupported(type) && !isNetworkProtected(type) &&
244                     ni != null && ni.isConnected();
245 
246             try {
247                 assertTrue("Network type " + type,
248                         mCm.requestRouteToHost(type, ipv4AddrToInt(HOST_ADDRESS3)) == expectToWork);
249             } catch (Exception e) {
250                 Log.d(TAG, "got exception in requestRouteToHost for type " + type);
251                 assertFalse("Exception received for type " + type, expectToWork);
252             }
253 
254             //TODO verify route table
255         }
256 
257         assertFalse(mCm.requestRouteToHost(-1, ipv4AddrToInt(HOST_ADDRESS1)));
258     }
259 }
260