• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
88     // Returns a list of all the IP addresses for all the networks of a given legacy type. We can't
89     // just fetch the IP addresses for that type because there is no public getLinkProperties API
90     // that takes a legacy type.
getIpAddresses(int type)91     private List<InetAddress> getIpAddresses(int type) {
92         ArrayList<InetAddress> addresses = new ArrayList<>();
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                 // This does not include IP addresses on stacked interfaces (e.g., 464xlat), because
98                 // there is no public API that will return them.
99                 LinkProperties lp = mCm.getLinkProperties(networks[i]);
100                 for (LinkAddress address : lp.getLinkAddresses()) {
101                     addresses.add(address.getAddress());
102                 }
103             }
104         }
105         return addresses;
106     }
107 
hasIPv4(int type)108     private boolean hasIPv4(int type) {
109         for (InetAddress address : getIpAddresses(type)) {
110             if (address instanceof Inet4Address) {
111                 return true;
112             }
113         }
114         return false;
115     }
116 
checkSourceAddress(String addrString, int type)117     private void checkSourceAddress(String addrString, int type) throws Exception {
118         // The public requestRouteToHost API only supports IPv4, but it will not return failure if
119         // the network does not have an IPv4 address. So don't check that it's working unless we
120         // know that the network has an IPv4 address. Note that it's possible that the network will
121         // have an IPv4 address but we don't know about it, because the IPv4 address might be on a
122         // stacked interface and we wouldn't be able to see it.
123         if (!hasIPv4(type)) {
124             Log.d(TAG, "Not checking source address on network type " + type + ", no IPv4 address");
125             return;
126         }
127 
128         DatagramSocket d = new DatagramSocket();
129         d.connect(InetAddress.getByName(addrString), 7);
130         InetAddress localAddress = d.getLocalAddress();
131         String localAddrString = localAddress.getHostAddress();
132 
133         Log.d(TAG, "Got source address " + localAddrString + " for destination " + addrString);
134 
135         assertTrue(
136                 "Local address " + localAddress + " not assigned to any network of type " + type,
137                 getIpAddresses(type).contains(localAddress));
138 
139         Log.d(TAG, "Source address " + localAddress + " found on network type " + type);
140     }
141 
142     /** Test that hipri can be brought up when Wifi is enabled. */
testStartUsingNetworkFeature_enableHipri()143     public void testStartUsingNetworkFeature_enableHipri() throws Exception {
144         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
145                 || !mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
146             // This test requires a mobile data connection and WiFi.
147             return;
148         }
149 
150         // Make sure WiFi is connected to an access point.
151         boolean isWifiEnabled = isWifiConnected();
152         try {
153             if (!isWifiEnabled) {
154                 connectToWifi();
155             }
156 
157             expectNetworkBroadcast(TYPE_MOBILE_HIPRI, NetworkInfo.State.CONNECTED, new Runnable() {
158                 public void run() {
159                     int ret = mCm.startUsingNetworkFeature(TYPE_MOBILE, FEATURE_ENABLE_HIPRI);
160                     assertTrue("Couldn't start using the HIPRI feature.", ret != -1);
161                 }
162             });
163 
164             assertTrue("Couldn't requestRouteToHost using HIPRI.",
165                     mCm.requestRouteToHost(TYPE_MOBILE_HIPRI, ipv4AddrToInt(HOST_ADDRESS1)));
166 
167             checkSourceAddress(HOST_ADDRESS1, TYPE_MOBILE);
168             checkSourceAddress(HOST_ADDRESS2, TYPE_WIFI);
169 
170             // TODO check dns selection
171 
172             expectNetworkBroadcast(TYPE_MOBILE_HIPRI, NetworkInfo.State.DISCONNECTED, new Runnable() {
173                 public void run() {
174                     int ret = mCm.stopUsingNetworkFeature(TYPE_MOBILE, FEATURE_ENABLE_HIPRI);
175                     assertTrue("Couldn't stop using the HIPRI feature.", ret != -1);
176                 }
177             });
178 
179             // TODO check dns selection
180         } finally {
181             if (!isWifiEnabled && isWifiConnected()) {
182                 disconnectFromWifi();
183             }
184         }
185     }
186 
testStartUsingNetworkFeature()187     public void testStartUsingNetworkFeature() {
188 
189         final String invalidFeature = "invalidFeature";
190         final String mmsFeature = "enableMMS";
191         final int failureCode = -1;
192         final int wifiOnlyStartFailureCode = APN_REQUEST_FAILED;
193         final int wifiOnlyStopFailureCode = -1;
194 
195         NetworkInfo ni = mCm.getNetworkInfo(TYPE_MOBILE);
196         if (ni != null) {
197             assertEquals(APN_REQUEST_FAILED,
198                     mCm.startUsingNetworkFeature(TYPE_MOBILE, invalidFeature));
199             assertEquals(failureCode, mCm.stopUsingNetworkFeature(TYPE_MOBILE, invalidFeature));
200         } else {
201             assertEquals(wifiOnlyStartFailureCode, mCm.startUsingNetworkFeature(TYPE_MOBILE,
202                     invalidFeature));
203             assertEquals(wifiOnlyStopFailureCode, mCm.stopUsingNetworkFeature(TYPE_MOBILE,
204                     invalidFeature));
205         }
206 
207         ni = mCm.getNetworkInfo(TYPE_WIFI);
208         if (ni != null) {
209             // Should return failure because MMS is not supported on WIFI.
210             assertEquals(APN_REQUEST_FAILED, mCm.startUsingNetworkFeature(TYPE_WIFI,
211                     mmsFeature));
212             assertEquals(failureCode, mCm.stopUsingNetworkFeature(TYPE_WIFI,
213                     mmsFeature));
214         }
215     }
216 
expectNetworkBroadcast(final int type, final NetworkInfo.State state, Runnable afterWhat)217     private void expectNetworkBroadcast(final int type, final NetworkInfo.State state,
218             Runnable afterWhat) {
219         final int TIMEOUT_MS = 30 * 1000;
220         final ConditionVariable var = new ConditionVariable();
221 
222         Log.d(TAG, "Waiting for " + state + " broadcast for type " + type);
223         BroadcastReceiver receiver = new BroadcastReceiver() {
224             public void onReceive(Context context, Intent intent) {
225                 NetworkInfo ni = intent.getExtras()
226                         .getParcelable(ConnectivityManager.EXTRA_NETWORK_INFO);
227                 assertNotNull("CONNECTIVITY_ACTION with null EXTRA_NETWORK_INFO", ni);
228                 if (ni.getType() == type && ni.getState().equals(state)) {
229                     Log.d(TAG, "Received expected " + state + " broadcast for type " + type);
230                     var.open();
231                 }
232             }
233         };
234         IntentFilter filter = new IntentFilter();
235         filter.addAction(CONNECTIVITY_ACTION);
236         mContext.registerReceiver(receiver, filter);
237 
238         try {
239             afterWhat.run();
240             final String msg = "Did not receive expected " + state + " broadcast for type " + type +
241                     " after " + TIMEOUT_MS + " ms";
242             assertTrue(msg, var.block(TIMEOUT_MS));
243         } finally {
244             mContext.unregisterReceiver(receiver);
245         }
246     }
247 
isWifiConnected()248     private boolean isWifiConnected() {
249         NetworkInfo ni = mCm.getNetworkInfo(TYPE_WIFI);
250         return ni != null && ni.isConnected();
251     }
252 
setWifiState(final boolean enabled)253     private void setWifiState(final boolean enabled) {
254         if (enabled != isWifiConnected()) {
255             final NetworkInfo.State desiredState = enabled ?
256                     NetworkInfo.State.CONNECTED :
257                     NetworkInfo.State.DISCONNECTED;
258             expectNetworkBroadcast(TYPE_WIFI, desiredState, new Runnable() {
259                 public void run() {
260                     mWifiManager.setWifiEnabled(enabled);
261                 }
262             });
263         }
264     }
265 
connectToWifi()266     private void connectToWifi() {
267         setWifiState(true);
268     }
269 
disconnectFromWifi()270     private void disconnectFromWifi() {
271         setWifiState(false);
272     }
273 
isNetworkSupported(int networkType)274     private boolean isNetworkSupported(int networkType) {
275         return mCm.getNetworkInfo(networkType) != null;
276     }
277 
testRequestRouteToHost()278     public void testRequestRouteToHost() throws Exception {
279         for (int type = -1 ; type <= MAX_NETWORK_TYPE; type++) {
280             NetworkInfo ni = mCm.getNetworkInfo(type);
281             boolean expectToWork = isNetworkSupported(type) && !isNetworkProtected(type) &&
282                     ni != null && ni.isConnected();
283 
284             try {
285                 assertTrue("Network type " + type,
286                         mCm.requestRouteToHost(type, ipv4AddrToInt(HOST_ADDRESS3)) == expectToWork);
287             } catch (Exception e) {
288                 Log.d(TAG, "got exception in requestRouteToHost for type " + type);
289                 assertFalse("Exception received for type " + type, expectToWork);
290             }
291 
292             //TODO verify route table
293         }
294 
295         assertFalse(mCm.requestRouteToHost(-1, ipv4AddrToInt(HOST_ADDRESS1)));
296     }
297 }
298