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