blob: 1836f061d057e7892ec9e9df28bea8ff0359c203 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net.cts.legacy.api22;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.ConditionVariable;
import android.test.AndroidTestCase;
import android.util.Log;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
public class ConnectivityManagerLegacyTest extends AndroidTestCase {
private static final String TAG = ConnectivityManagerLegacyTest.class.getSimpleName();
private static final String FEATURE_ENABLE_HIPRI = "enableHIPRI";
private static final String HOST_ADDRESS1 = "192.0.2.1";
private static final String HOST_ADDRESS2 = "192.0.2.2";
private static final String HOST_ADDRESS3 = "192.0.2.3";
// These are correct as of API level 22, which is what we target here.
private static final int APN_REQUEST_FAILED = 3;
private static final int MAX_NETWORK_TYPE = TYPE_VPN;
private ConnectivityManager mCm;
private WifiManager mWifiManager;
private PackageManager mPackageManager;
private final List<Integer>mProtectedNetworks = new ArrayList<Integer>();
protected void setUp() throws Exception {
super.setUp();
mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
mPackageManager = getContext().getPackageManager();
// Get com.android.internal.R.array.config_protectedNetworks
int resId = getContext().getResources().getIdentifier("config_protectedNetworks", "array", "android");
int[] protectedNetworks = getContext().getResources().getIntArray(resId);
for (int p : protectedNetworks) {
mProtectedNetworks.add(p);
}
}
// true if only the system can turn it on
private boolean isNetworkProtected(int networkType) {
return mProtectedNetworks.contains(networkType);
}
private int ipv4AddrToInt(String addrString) throws Exception {
byte[] addr = ((Inet4Address) InetAddress.getByName(addrString)).getAddress();
return ((addr[3] & 0xff) << 24) | ((addr[2] & 0xff) << 16) |
((addr[1] & 0xff) << 8) | (addr[0] & 0xff);
}
// Returns a list of all the IP addresses for all the networks of a given legacy type. We can't
// just fetch the IP addresses for that type because there is no public getLinkProperties API
// that takes a legacy type.
private List<InetAddress> getIpAddresses(int type) {
ArrayList<InetAddress> addresses = new ArrayList<>();
Network[] networks = mCm.getAllNetworks();
for (int i = 0; i < networks.length; i++) {
NetworkInfo ni = mCm.getNetworkInfo(networks[i]);
if (ni != null && ni.getType() == type) {
// This does not include IP addresses on stacked interfaces (e.g., 464xlat), because
// there is no public API that will return them.
LinkProperties lp = mCm.getLinkProperties(networks[i]);
for (LinkAddress address : lp.getLinkAddresses()) {
addresses.add(address.getAddress());
}
}
}
return addresses;
}
private boolean hasIPv4(int type) {
for (InetAddress address : getIpAddresses(type)) {
if (address instanceof Inet4Address) {
return true;
}
}
return false;
}
private void checkSourceAddress(String addrString, int type) throws Exception {
// The public requestRouteToHost API only supports IPv4, but it will not return failure if
// the network does not have an IPv4 address. So don't check that it's working unless we
// know that the network has an IPv4 address. Note that it's possible that the network will
// have an IPv4 address but we don't know about it, because the IPv4 address might be on a
// stacked interface and we wouldn't be able to see it.
if (!hasIPv4(type)) {
Log.d(TAG, "Not checking source address on network type " + type + ", no IPv4 address");
return;
}
DatagramSocket d = new DatagramSocket();
d.connect(InetAddress.getByName(addrString), 7);
InetAddress localAddress = d.getLocalAddress();
String localAddrString = localAddress.getHostAddress();
Log.d(TAG, "Got source address " + localAddrString + " for destination " + addrString);
assertTrue(
"Local address " + localAddress + " not assigned to any network of type " + type,
getIpAddresses(type).contains(localAddress));
Log.d(TAG, "Source address " + localAddress + " found on network type " + type);
}
/** Test that hipri can be brought up when Wifi is enabled. */
public void testStartUsingNetworkFeature_enableHipri() throws Exception {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
|| !mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
// This test requires a mobile data connection and WiFi.
return;
}
// Make sure WiFi is connected to an access point.
connectToWifi();
expectNetworkBroadcast(TYPE_MOBILE_HIPRI, NetworkInfo.State.CONNECTED,
new Runnable() {
public void run() {
int ret = mCm.startUsingNetworkFeature(TYPE_MOBILE, FEATURE_ENABLE_HIPRI);
assertTrue("Couldn't start using the HIPRI feature.", ret != -1);
}
});
assertTrue("Couldn't requestRouteToHost using HIPRI.",
mCm.requestRouteToHost(TYPE_MOBILE_HIPRI, ipv4AddrToInt(HOST_ADDRESS1)));
checkSourceAddress(HOST_ADDRESS1, TYPE_MOBILE);
checkSourceAddress(HOST_ADDRESS2, TYPE_WIFI);
// TODO check dns selection
expectNetworkBroadcast(TYPE_MOBILE_HIPRI, NetworkInfo.State.DISCONNECTED,
new Runnable() {
public void run() {
int ret = mCm.stopUsingNetworkFeature(TYPE_MOBILE, FEATURE_ENABLE_HIPRI);
assertTrue("Couldn't stop using the HIPRI feature.", ret != -1);
}
});
// TODO check dns selection
disconnectFromWifi();
}
public void testStartUsingNetworkFeature() {
final String invalidFeature = "invalidFeature";
final String mmsFeature = "enableMMS";
final int failureCode = -1;
final int wifiOnlyStartFailureCode = APN_REQUEST_FAILED;
final int wifiOnlyStopFailureCode = -1;
NetworkInfo ni = mCm.getNetworkInfo(TYPE_MOBILE);
if (ni != null) {
assertEquals(APN_REQUEST_FAILED,
mCm.startUsingNetworkFeature(TYPE_MOBILE, invalidFeature));
assertEquals(failureCode, mCm.stopUsingNetworkFeature(TYPE_MOBILE, invalidFeature));
} else {
assertEquals(wifiOnlyStartFailureCode, mCm.startUsingNetworkFeature(TYPE_MOBILE,
invalidFeature));
assertEquals(wifiOnlyStopFailureCode, mCm.stopUsingNetworkFeature(TYPE_MOBILE,
invalidFeature));
}
ni = mCm.getNetworkInfo(TYPE_WIFI);
if (ni != null) {
// Should return failure because MMS is not supported on WIFI.
assertEquals(APN_REQUEST_FAILED, mCm.startUsingNetworkFeature(TYPE_WIFI,
mmsFeature));
assertEquals(failureCode, mCm.stopUsingNetworkFeature(TYPE_WIFI,
mmsFeature));
}
}
private void expectNetworkBroadcast(final int type, final NetworkInfo.State state,
Runnable afterWhat) {
final int TIMEOUT_MS = 30 * 1000;
final ConditionVariable var = new ConditionVariable();
Log.d(TAG, "Waiting for " + state + " broadcast for type " + type);
BroadcastReceiver receiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
NetworkInfo ni = intent.getExtras()
.getParcelable(ConnectivityManager.EXTRA_NETWORK_INFO);
assertNotNull("CONNECTIVITY_ACTION with null EXTRA_NETWORK_INFO", ni);
if (ni.getType() == type && ni.getState().equals(state)) {
Log.d(TAG, "Received expected " + state + " broadcast for type " + type);
var.open();
}
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(CONNECTIVITY_ACTION);
mContext.registerReceiver(receiver, filter);
try {
afterWhat.run();
final String msg = "Did not receive expected " + state + " broadcast for type " + type +
" after " + TIMEOUT_MS + " ms";
assertTrue(msg, var.block(TIMEOUT_MS));
} finally {
mContext.unregisterReceiver(receiver);
}
}
private boolean isWifiConnected() {
NetworkInfo ni = mCm.getNetworkInfo(TYPE_WIFI);
return ni != null && ni.isConnected();
}
private void setWifiState(final boolean enabled) {
if (enabled != isWifiConnected()) {
final NetworkInfo.State desiredState = enabled ?
NetworkInfo.State.CONNECTED :
NetworkInfo.State.DISCONNECTED;
expectNetworkBroadcast(TYPE_WIFI, desiredState, new Runnable() {
public void run() {
mWifiManager.setWifiEnabled(enabled);
}
});
}
}
private void connectToWifi() {
setWifiState(true);
}
private void disconnectFromWifi() {
setWifiState(false);
}
private boolean isNetworkSupported(int networkType) {
return mCm.getNetworkInfo(networkType) != null;
}
public void testRequestRouteToHost() throws Exception {
for (int type = -1 ; type <= MAX_NETWORK_TYPE; type++) {
NetworkInfo ni = mCm.getNetworkInfo(type);
boolean expectToWork = isNetworkSupported(type) && !isNetworkProtected(type) &&
ni != null && ni.isConnected();
try {
assertTrue("Network type " + type,
mCm.requestRouteToHost(type, ipv4AddrToInt(HOST_ADDRESS3)) == expectToWork);
} catch (Exception e) {
Log.d(TAG, "got exception in requestRouteToHost for type " + type);
assertFalse("Exception received for type " + type, expectToWork);
}
//TODO verify route table
}
assertFalse(mCm.requestRouteToHost(-1, ipv4AddrToInt(HOST_ADDRESS1)));
}
}