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.telephony.ServiceState;
30 import android.telephony.TelephonyManager;
31 import android.os.ConditionVariable;
32 import android.test.AndroidTestCase;
33 import android.util.Log;
34 
35 import com.android.compatibility.common.util.SystemUtil;
36 
37 import java.net.DatagramSocket;
38 import java.net.Inet4Address;
39 import java.net.InetAddress;
40 import java.util.ArrayList;
41 import java.util.List;
42 
43 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
44 import static android.net.ConnectivityManager.TYPE_MOBILE;
45 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
46 import static android.net.ConnectivityManager.TYPE_VPN;
47 import static android.net.ConnectivityManager.TYPE_WIFI;
48 
49 public class ConnectivityManagerLegacyTest extends AndroidTestCase {
50     private static final String TAG = ConnectivityManagerLegacyTest.class.getSimpleName();
51     private static final String FEATURE_ENABLE_HIPRI = "enableHIPRI";
52     private static final String HOST_ADDRESS1 = "192.0.2.1";
53     private static final String HOST_ADDRESS2 = "192.0.2.2";
54     private static final String HOST_ADDRESS3 = "192.0.2.3";
55     private static final String HOST_ADDRESS4 = "192.0.2.4";
56 
57     // These are correct as of API level 22, which is what we target here.
58     private static final int APN_REQUEST_FAILED = 3;
59     private static final int MAX_NETWORK_TYPE = TYPE_VPN;
60 
61     private ConnectivityManager mCm;
62     private PackageManager mPackageManager;
63     private TelephonyManager mTm;
64 
65     private final List<Integer>mProtectedNetworks = new ArrayList<Integer>();
66 
setUp()67     protected void setUp() throws Exception {
68         super.setUp();
69         mCm = getContext().getSystemService(ConnectivityManager.class);
70 
71         mPackageManager = getContext().getPackageManager();
72         mTm = getContext().getSystemService(TelephonyManager.class);
73 
74         // Get com.android.internal.R.array.config_protectedNetworks
75         int resId = getContext().getResources().getIdentifier("config_protectedNetworks", "array", "android");
76         int[] protectedNetworks = getContext().getResources().getIntArray(resId);
77         for (int p : protectedNetworks) {
78             mProtectedNetworks.add(p);
79         }
80     }
81 
82     // true if only the system can turn it on
isNetworkProtected(int networkType)83     private boolean isNetworkProtected(int networkType) {
84         return mProtectedNetworks.contains(networkType);
85     }
86 
ipv4AddrToInt(String addrString)87     private int ipv4AddrToInt(String addrString) throws Exception {
88         byte[] addr = ((Inet4Address) InetAddress.getByName(addrString)).getAddress();
89         return ((addr[3] & 0xff) << 24) | ((addr[2] & 0xff) << 16) |
90                 ((addr[1] & 0xff) << 8) | (addr[0] & 0xff);
91     }
92 
93     // Returns a list of all the IP addresses for all the networks of a given legacy type. We can't
94     // just fetch the IP addresses for that type because there is no public getLinkProperties API
95     // that takes a legacy type.
getIpAddresses(int type)96     private List<InetAddress> getIpAddresses(int type) {
97         ArrayList<InetAddress> addresses = new ArrayList<>();
98         Network[] networks = mCm.getAllNetworks();
99         for (int i = 0; i < networks.length; i++) {
100             NetworkInfo ni = mCm.getNetworkInfo(networks[i]);
101             if (ni != null && ni.getType() == type) {
102                 // This does not include IP addresses on stacked interfaces (e.g., 464xlat), because
103                 // there is no public API that will return them.
104                 LinkProperties lp = mCm.getLinkProperties(networks[i]);
105                 for (LinkAddress address : lp.getLinkAddresses()) {
106                     addresses.add(address.getAddress());
107                 }
108             }
109         }
110         return addresses;
111     }
112 
hasIPv4(int type)113     private boolean hasIPv4(int type) {
114         for (InetAddress address : getIpAddresses(type)) {
115             if (address instanceof Inet4Address) {
116                 return true;
117             }
118         }
119         return false;
120     }
121 
checkSourceAddress(String addrString, int type)122     private void checkSourceAddress(String addrString, int type) throws Exception {
123         // The public requestRouteToHost API only supports IPv4, but it will not return failure if
124         // the network does not have an IPv4 address. So don't check that it's working unless we
125         // know that the network has an IPv4 address. Note that it's possible that the network will
126         // have an IPv4 address but we don't know about it, because the IPv4 address might be on a
127         // stacked interface and we wouldn't be able to see it.
128         if (!hasIPv4(type)) {
129             Log.d(TAG, "Not checking source address on network type " + type + ", no IPv4 address");
130             return;
131         }
132 
133         DatagramSocket d = new DatagramSocket();
134         d.connect(InetAddress.getByName(addrString), 7);
135         InetAddress localAddress = d.getLocalAddress();
136         String localAddrString = localAddress.getHostAddress();
137 
138         Log.d(TAG, "Got source address " + localAddrString + " for destination " + addrString);
139 
140         assertTrue(
141                 "Local address " + localAddress + " not assigned to any network of type " + type,
142                 getIpAddresses(type).contains(localAddress));
143 
144         Log.d(TAG, "Source address " + localAddress + " found on network type " + type);
145     }
146 
assertTelephonyInService()147     private void assertTelephonyInService() {
148         final ServiceState state = mTm.getServiceState();
149         if (state == null || state.getState() != ServiceState.STATE_IN_SERVICE) {
150             fail("Telephony state is out of service. Please ensure device has a working SIM card.");
151         }
152     }
153 
154     /** Test that hipri can be brought up when Wifi is enabled. */
testStartUsingNetworkFeature_enableHipri()155     public void testStartUsingNetworkFeature_enableHipri() throws Exception {
156         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
157                 || !mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
158             // This test requires a mobile data connection and WiFi.
159             return;
160         }
161 
162         assertTelephonyInService();
163 
164         // Make sure WiFi is connected to an access point.
165         boolean isWifiEnabled = isWifiConnected();
166         try {
167             if (!isWifiEnabled) {
168                 connectToWifi();
169             }
170 
171             expectNetworkBroadcast(TYPE_MOBILE_HIPRI, NetworkInfo.State.CONNECTED, new Runnable() {
172                 public void run() {
173                     int ret = mCm.startUsingNetworkFeature(TYPE_MOBILE, FEATURE_ENABLE_HIPRI);
174                     assertTrue("Couldn't start using the HIPRI feature.", ret != -1);
175                 }
176             });
177 
178             assertTrue("Couldn't requestRouteToHost using HIPRI.",
179                     mCm.requestRouteToHost(TYPE_MOBILE_HIPRI, ipv4AddrToInt(HOST_ADDRESS1)));
180 
181             assertTrue("Couldn't requestRouteToHostAddress using HIPRI.",
182                     mCm.requestRouteToHostAddress(TYPE_MOBILE_HIPRI,
183                             InetAddress.getByName(HOST_ADDRESS3)));
184 
185             checkSourceAddress(HOST_ADDRESS1, TYPE_MOBILE);
186             checkSourceAddress(HOST_ADDRESS2, TYPE_WIFI);
187             checkSourceAddress(HOST_ADDRESS3, TYPE_MOBILE);
188 
189             // TODO check dns selection
190 
191             expectNetworkBroadcast(TYPE_MOBILE_HIPRI, NetworkInfo.State.DISCONNECTED, new Runnable() {
192                 public void run() {
193                     int ret = mCm.stopUsingNetworkFeature(TYPE_MOBILE, FEATURE_ENABLE_HIPRI);
194                     assertTrue("Couldn't stop using the HIPRI feature.", ret != -1);
195                 }
196             });
197 
198             // TODO check dns selection
199         } finally {
200             if (!isWifiEnabled && isWifiConnected()) {
201                 disconnectFromWifi();
202             }
203         }
204     }
205 
testStartUsingNetworkFeature()206     public void testStartUsingNetworkFeature() {
207 
208         final String invalidFeature = "invalidFeature";
209         final String mmsFeature = "enableMMS";
210         final int failureCode = -1;
211         final int wifiOnlyStartFailureCode = APN_REQUEST_FAILED;
212         final int wifiOnlyStopFailureCode = -1;
213 
214         NetworkInfo ni = mCm.getNetworkInfo(TYPE_MOBILE);
215         if (ni != null) {
216             assertEquals(APN_REQUEST_FAILED,
217                     mCm.startUsingNetworkFeature(TYPE_MOBILE, invalidFeature));
218             assertEquals(failureCode, mCm.stopUsingNetworkFeature(TYPE_MOBILE, invalidFeature));
219         } else {
220             assertEquals(wifiOnlyStartFailureCode, mCm.startUsingNetworkFeature(TYPE_MOBILE,
221                     invalidFeature));
222             assertEquals(wifiOnlyStopFailureCode, mCm.stopUsingNetworkFeature(TYPE_MOBILE,
223                     invalidFeature));
224         }
225 
226         ni = mCm.getNetworkInfo(TYPE_WIFI);
227         if (ni != null) {
228             // Should return failure because MMS is not supported on WIFI.
229             assertEquals(APN_REQUEST_FAILED, mCm.startUsingNetworkFeature(TYPE_WIFI,
230                     mmsFeature));
231             assertEquals(failureCode, mCm.stopUsingNetworkFeature(TYPE_WIFI,
232                     mmsFeature));
233         }
234     }
235 
expectNetworkBroadcast(final int type, final NetworkInfo.State state, Runnable afterWhat)236     private void expectNetworkBroadcast(final int type, final NetworkInfo.State state,
237             Runnable afterWhat) {
238         final int TIMEOUT_MS = 30 * 1000;
239         final ConditionVariable var = new ConditionVariable();
240 
241         Log.d(TAG, "Waiting for " + state + " broadcast for type " + type);
242         BroadcastReceiver receiver = new BroadcastReceiver() {
243             public void onReceive(Context context, Intent intent) {
244                 NetworkInfo ni = intent.getExtras()
245                         .getParcelable(ConnectivityManager.EXTRA_NETWORK_INFO);
246                 assertNotNull("CONNECTIVITY_ACTION with null EXTRA_NETWORK_INFO", ni);
247                 if (ni.getType() == type && ni.getState().equals(state)) {
248                     Log.d(TAG, "Received expected " + state + " broadcast for type " + type);
249                     var.open();
250                 }
251             }
252         };
253         IntentFilter filter = new IntentFilter();
254         filter.addAction(CONNECTIVITY_ACTION);
255         mContext.registerReceiver(receiver, filter);
256 
257         try {
258             afterWhat.run();
259             final String msg = "Did not receive expected " + state + " broadcast for type " + type +
260                     " after " + TIMEOUT_MS + " ms";
261             assertTrue(msg, var.block(TIMEOUT_MS));
262         } finally {
263             mContext.unregisterReceiver(receiver);
264         }
265     }
266 
isWifiConnected()267     private boolean isWifiConnected() {
268         NetworkInfo ni = mCm.getNetworkInfo(TYPE_WIFI);
269         return ni != null && ni.isConnected();
270     }
271 
setWifiState(final boolean enabled)272     private void setWifiState(final boolean enabled) {
273         if (enabled != isWifiConnected()) {
274             final NetworkInfo.State desiredState = enabled ?
275                     NetworkInfo.State.CONNECTED :
276                     NetworkInfo.State.DISCONNECTED;
277             expectNetworkBroadcast(TYPE_WIFI, desiredState, new Runnable() {
278                 public void run() {
279                     SystemUtil.runShellCommand("svc wifi " + (enabled ? "enable" : "disable"));
280                 }
281             });
282         }
283     }
284 
connectToWifi()285     private void connectToWifi() {
286         setWifiState(true);
287     }
288 
disconnectFromWifi()289     private void disconnectFromWifi() {
290         setWifiState(false);
291     }
292 
isNetworkSupported(int networkType)293     private boolean isNetworkSupported(int networkType) {
294         return mCm.getNetworkInfo(networkType) != null;
295     }
296 
testRequestRouteToHost()297     public void testRequestRouteToHost() throws Exception {
298         for (int type = -1 ; type <= MAX_NETWORK_TYPE; type++) {
299             NetworkInfo ni = mCm.getNetworkInfo(type);
300             boolean expectToWork = isNetworkSupported(type) && !isNetworkProtected(type) &&
301                     ni != null && ni.isConnected();
302 
303             try {
304                 assertTrue("Network type " + type,
305                         mCm.requestRouteToHost(type, ipv4AddrToInt(HOST_ADDRESS4)) == expectToWork);
306             } catch (Exception e) {
307                 Log.d(TAG, "got exception in requestRouteToHost for type " + type);
308                 assertFalse("Exception received for type " + type, expectToWork);
309             }
310 
311             //TODO verify route table
312         }
313 
314         assertFalse(mCm.requestRouteToHost(-1, ipv4AddrToInt(HOST_ADDRESS4)));
315     }
316 }
317