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