blob: 9a99c22c07136fb011988e004b9cd47a41c0105d [file] [log] [blame]
/*
* Copyright (C) 2009 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;
import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import android.app.PendingIntent;
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.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
import android.net.NetworkRequest;
import android.net.wifi.WifiManager;
import android.test.AndroidTestCase;
import android.util.Log;
import android.os.SystemProperties;
import com.android.internal.telephony.PhoneConstants;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class ConnectivityManagerTest extends AndroidTestCase {
private static final String TAG = ConnectivityManagerTest.class.getSimpleName();
private static final String FEATURE_ENABLE_HIPRI = "enableHIPRI";
public static final int TYPE_MOBILE = ConnectivityManager.TYPE_MOBILE;
public static final int TYPE_WIFI = ConnectivityManager.TYPE_WIFI;
private static final int HOST_ADDRESS = 0x7f000001;// represent ip 127.0.0.1
// Action sent to ConnectivityActionReceiver when a network callback is sent via PendingIntent.
private static final String NETWORK_CALLBACK_ACTION =
"ConnectivityManagerTest.NetworkCallbackAction";
// device could have only one interface: data, wifi.
private static final int MIN_NUM_NETWORK_TYPES = 1;
private ConnectivityManager mCm;
private WifiManager mWifiManager;
private PackageManager mPackageManager;
private final HashMap<Integer, NetworkConfig> mNetworks =
new HashMap<Integer, NetworkConfig>();
@Override
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.networkAttributes
int resId = getContext().getResources().getIdentifier("networkAttributes", "array", "android");
String[] naStrings = getContext().getResources().getStringArray(resId);
//TODO: What is the "correct" way to determine if this is a wifi only device?
boolean wifiOnly = SystemProperties.getBoolean("ro.radio.noril", false);
for (String naString : naStrings) {
try {
NetworkConfig n = new NetworkConfig(naString);
if (wifiOnly && ConnectivityManager.isNetworkTypeMobile(n.type)) {
continue;
}
mNetworks.put(n.type, n);
} catch (Exception e) {}
}
}
public void testIsNetworkTypeValid() {
assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE));
assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_WIFI));
assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_MMS));
assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_SUPL));
assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_DUN));
assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_HIPRI));
assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_WIMAX));
assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_BLUETOOTH));
assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_DUMMY));
assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_ETHERNET));
assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_FOTA));
assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_IMS));
assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_CBS));
assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_WIFI_P2P));
assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_IA));
assertFalse(mCm.isNetworkTypeValid(-1));
assertTrue(mCm.isNetworkTypeValid(0));
assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.MAX_NETWORK_TYPE));
assertFalse(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.MAX_NETWORK_TYPE+1));
NetworkInfo[] ni = mCm.getAllNetworkInfo();
for (NetworkInfo n: ni) {
assertTrue(ConnectivityManager.isNetworkTypeValid(n.getType()));
}
}
public void testSetNetworkPreference() {
// getNetworkPreference() and setNetworkPreference() are both deprecated so they do
// not preform any action. Verify they are at least still callable.
mCm.setNetworkPreference(mCm.getNetworkPreference());
}
public void testGetActiveNetworkInfo() {
NetworkInfo ni = mCm.getActiveNetworkInfo();
assertNotNull("You must have an active network connection to complete CTS", ni);
assertTrue(ConnectivityManager.isNetworkTypeValid(ni.getType()));
assertTrue(ni.getState() == State.CONNECTED);
}
public void testGetActiveNetwork() {
Network network = mCm.getActiveNetwork();
assertNotNull("You must have an active network connection to complete CTS", network);
NetworkInfo ni = mCm.getNetworkInfo(network);
assertNotNull("Network returned from getActiveNetwork was invalid", ni);
// Similar to testGetActiveNetworkInfo above.
assertTrue(ConnectivityManager.isNetworkTypeValid(ni.getType()));
assertTrue(ni.getState() == State.CONNECTED);
}
public void testGetNetworkInfo() {
for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE+1; type++) {
if (isSupported(type)) {
NetworkInfo ni = mCm.getNetworkInfo(type);
assertTrue("Info shouldn't be null for " + type, ni != null);
State state = ni.getState();
assertTrue("Bad state for " + type, State.UNKNOWN.ordinal() >= state.ordinal()
&& state.ordinal() >= State.CONNECTING.ordinal());
DetailedState ds = ni.getDetailedState();
assertTrue("Bad detailed state for " + type,
DetailedState.FAILED.ordinal() >= ds.ordinal()
&& ds.ordinal() >= DetailedState.IDLE.ordinal());
} else {
assertNull("Info should be null for " + type, mCm.getNetworkInfo(type));
}
}
}
public void testGetAllNetworkInfo() {
NetworkInfo[] ni = mCm.getAllNetworkInfo();
assertTrue(ni.length >= MIN_NUM_NETWORK_TYPES);
for (int type = 0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
int desiredFoundCount = (isSupported(type) ? 1 : 0);
int foundCount = 0;
for (NetworkInfo i : ni) {
if (i.getType() == type) foundCount++;
}
if (foundCount != desiredFoundCount) {
Log.e(TAG, "failure in testGetAllNetworkInfo. Dump of returned NetworkInfos:");
for (NetworkInfo networkInfo : ni) Log.e(TAG, " " + networkInfo);
}
assertTrue("Unexpected foundCount of " + foundCount + " for type " + type,
foundCount == desiredFoundCount);
}
}
private void assertStartUsingNetworkFeatureUnsupported(int networkType, String feature) {
try {
mCm.startUsingNetworkFeature(networkType, feature);
fail("startUsingNetworkFeature is no longer supported in the current API version");
} catch (UnsupportedOperationException expected) {}
}
private void assertStopUsingNetworkFeatureUnsupported(int networkType, String feature) {
try {
mCm.startUsingNetworkFeature(networkType, feature);
fail("stopUsingNetworkFeature is no longer supported in the current API version");
} catch (UnsupportedOperationException expected) {}
}
private void assertRequestRouteToHostUnsupported(int networkType, int hostAddress) {
try {
mCm.requestRouteToHost(networkType, hostAddress);
fail("requestRouteToHost is no longer supported in the current API version");
} catch (UnsupportedOperationException expected) {}
}
public void testStartUsingNetworkFeature() {
final String invalidateFeature = "invalidateFeature";
final String mmsFeature = "enableMMS";
final int failureCode = -1;
final int wifiOnlyStartFailureCode = PhoneConstants.APN_REQUEST_FAILED;
final int wifiOnlyStopFailureCode = -1;
assertStartUsingNetworkFeatureUnsupported(TYPE_MOBILE, invalidateFeature);
assertStopUsingNetworkFeatureUnsupported(TYPE_MOBILE, invalidateFeature);
assertStartUsingNetworkFeatureUnsupported(TYPE_WIFI, mmsFeature);
}
private boolean isSupported(int networkType) {
// Change-Id I02eb5f22737720095f646f8db5c87fd66da129d6 added VPN support
// to all devices directly in software, independent of any external
// configuration.
return mNetworks.containsKey(networkType) ||
(networkType == ConnectivityManager.TYPE_VPN);
}
public void testIsNetworkSupported() {
for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
boolean supported = mCm.isNetworkSupported(type);
if (isSupported(type)) {
assertTrue(supported);
} else {
assertFalse(supported);
}
}
}
public void testRequestRouteToHost() {
for (int type = -1 ; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
assertRequestRouteToHostUnsupported(type, HOST_ADDRESS);
}
}
public void testTest() {
mCm.getBackgroundDataSetting();
}
/**
* Exercises both registerNetworkCallback and unregisterNetworkCallback. This checks to
* see if we get a callback for the TRANSPORT_WIFI transport type being available.
*
* <p>In order to test that a NetworkCallback occurs, we need some change in the network
* state (either a transport or capability is now available). The most straightforward is
* WiFi. We could add a version that uses the telephony data connection but it's not clear
* that it would increase test coverage by much (how many devices have 3G radio but not Wifi?).
*/
public void testRegisterNetworkCallback() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
return;
}
// We will register for a WIFI network being available or lost.
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build();
TestNetworkCallback callback = new TestNetworkCallback();
mCm.registerNetworkCallback(request, callback);
boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
try {
// Make sure WiFi is connected to an access point to start with.
if (!previousWifiEnabledState) {
connectToWifi();
}
// Now we should expect to get a network callback about availability of the wifi
// network even if it was already connected as a state-based action when the callback
// is registered.
assertTrue("Did not receive NetworkCallback.onAvailable for TRANSPORT_WIFI",
callback.waitForAvailable());
} catch (InterruptedException e) {
fail("Broadcast receiver or NetworkCallback wait was interrupted.");
} finally {
mCm.unregisterNetworkCallback(callback);
// Return WiFI to its original enabled/disabled state.
if (!previousWifiEnabledState) {
disconnectFromWifi();
}
}
}
/**
* Tests both registerNetworkCallback and unregisterNetworkCallback similarly to
* {@link #testRegisterNetworkCallback} except that a {@code PendingIntent} is used instead
* of a {@code NetworkCallback}.
*/
public void testRegisterNetworkCallback_withPendingIntent() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
return;
}
// Create a ConnectivityActionReceiver that has an IntentFilter for our locally defined
// action, NETWORK_CALLBACK_ACTION.
IntentFilter filter = new IntentFilter();
filter.addAction(NETWORK_CALLBACK_ACTION);
ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED);
mContext.registerReceiver(receiver, filter);
// Create a broadcast PendingIntent for NETWORK_CALLBACK_ACTION.
Intent intent = new Intent(NETWORK_CALLBACK_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
// We will register for a WIFI network being available or lost.
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build();
mCm.registerNetworkCallback(request, pendingIntent);
boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
try {
// Make sure WiFi is connected to an access point to start with.
if (!previousWifiEnabledState) {
connectToWifi();
}
// Now we expect to get the Intent delivered notifying of the availability of the wifi
// network even if it was already connected as a state-based action when the callback
// is registered.
assertTrue("Did not receive expected Intent " + intent + " for TRANSPORT_WIFI",
receiver.waitForState());
} catch (InterruptedException e) {
fail("Broadcast receiver or NetworkCallback wait was interrupted.");
} finally {
mCm.unregisterNetworkCallback(pendingIntent);
pendingIntent.cancel();
mContext.unregisterReceiver(receiver);
// Return WiFI to its original enabled/disabled state.
if (!previousWifiEnabledState) {
disconnectFromWifi();
}
}
}
/** Enable WiFi and wait for it to become connected to a network. */
private void connectToWifi() {
ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED);
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mContext.registerReceiver(receiver, filter);
boolean connected = false;
try {
assertTrue(mWifiManager.setWifiEnabled(true));
connected = receiver.waitForState();
} catch (InterruptedException ex) {
fail("connectToWifi was interrupted");
} finally {
mContext.unregisterReceiver(receiver);
}
assertTrue("Wifi must be configured to connect to an access point for this test.",
connected);
}
/** Disable WiFi and wait for it to become disconnected from the network. */
private void disconnectFromWifi() {
ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
ConnectivityManager.TYPE_WIFI, NetworkInfo.State.DISCONNECTED);
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mContext.registerReceiver(receiver, filter);
boolean disconnected = false;
try {
assertTrue(mWifiManager.setWifiEnabled(false));
disconnected = receiver.waitForState();
} catch (InterruptedException ex) {
fail("disconnectFromWifi was interrupted");
} finally {
mContext.unregisterReceiver(receiver);
}
assertTrue("Wifi failed to reach DISCONNECTED state.", disconnected);
}
/**
* Receiver that captures the last connectivity change's network type and state. Recognizes
* both {@code CONNECTIVITY_ACTION} and {@code NETWORK_CALLBACK_ACTION} intents.
*/
private class ConnectivityActionReceiver extends BroadcastReceiver {
private final CountDownLatch mReceiveLatch = new CountDownLatch(1);
private final int mNetworkType;
private final NetworkInfo.State mNetState;
ConnectivityActionReceiver(int networkType, NetworkInfo.State netState) {
mNetworkType = networkType;
mNetState = netState;
}
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
NetworkInfo networkInfo = null;
// When receiving ConnectivityManager.CONNECTIVITY_ACTION, the NetworkInfo parcelable
// is stored in EXTRA_NETWORK_INFO. With a NETWORK_CALLBACK_ACTION, the Network is
// sent in EXTRA_NETWORK and we need to ask the ConnectivityManager for the NetworkInfo.
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
networkInfo = intent.getExtras()
.getParcelable(ConnectivityManager.EXTRA_NETWORK_INFO);
assertNotNull("ConnectivityActionReceiver expected EXTRA_NETWORK_INFO", networkInfo);
} else if (NETWORK_CALLBACK_ACTION.equals(action)) {
Network network = intent.getExtras()
.getParcelable(ConnectivityManager.EXTRA_NETWORK);
assertNotNull("ConnectivityActionReceiver expected EXTRA_NETWORK", network);
networkInfo = mCm.getNetworkInfo(network);
if (networkInfo == null) {
// When disconnecting, it seems like we get an intent sent with an invalid
// Network; that is, by the time we call ConnectivityManager.getNetworkInfo(),
// it is invalid. Ignore these.
Log.i(TAG, "ConnectivityActionReceiver NETWORK_CALLBACK_ACTION ignoring "
+ "invalid network");
return;
}
} else {
fail("ConnectivityActionReceiver received unxpected intent action: " + action);
}
assertNotNull("ConnectivityActionReceiver didn't find NetworkInfo", networkInfo);
int networkType = networkInfo.getType();
State networkState = networkInfo.getState();
Log.i(TAG, "Network type: " + networkType + " state: " + networkState);
if (networkType == mNetworkType && networkInfo.getState() == mNetState) {
mReceiveLatch.countDown();
}
}
public boolean waitForState() throws InterruptedException {
return mReceiveLatch.await(30, TimeUnit.SECONDS);
}
}
/**
* Callback used in testRegisterNetworkCallback that allows caller to block on
* {@code onAvailable}.
*/
private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
private final CountDownLatch mAvailableLatch = new CountDownLatch(1);
public boolean waitForAvailable() throws InterruptedException {
return mAvailableLatch.await(30, TimeUnit.SECONDS);
}
@Override
public void onAvailable(Network network) {
mAvailableLatch.countDown();
}
}
/** Verify restricted networks cannot be requested. */
public void testRestrictedNetworks() {
// Verify we can request unrestricted networks:
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_INTERNET).build();
NetworkCallback callback = new NetworkCallback();
mCm.requestNetwork(request, callback);
mCm.unregisterNetworkCallback(callback);
// Verify we cannot request restricted networks:
request = new NetworkRequest.Builder().addCapability(NET_CAPABILITY_IMS).build();
callback = new NetworkCallback();
try {
mCm.requestNetwork(request, callback);
fail("No exception thrown when restricted network requested.");
} catch (SecurityException e) {
// Expected.
}
}
}