summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThe Android Automerger <android-build@android.com>2010-11-16 16:02:35 -0800
committerThe Android Automerger <android-build@android.com>2010-11-16 16:02:35 -0800
commit0f53988424d044e8dc2705379dfc8337a56427a9 (patch)
tree879c8f553305d801db6a02aa16778ad2dea64d0c
parent483f658e83f822335144d94203b4a72a35e6952f (diff)
parentd0a8f54a5bcd6a10eaaa88d888c3cbb83869532c (diff)
downloadbase-0f53988424d044e8dc2705379dfc8337a56427a9.tar.gz
Merge branch 'gingerbread' into gingerbread-release
-rwxr-xr-xcore/tests/ConnectivityManagerTest/assets/accesspoints.xml25
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java262
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java131
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java2
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java188
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java134
6 files changed, 610 insertions, 132 deletions
diff --git a/core/tests/ConnectivityManagerTest/assets/accesspoints.xml b/core/tests/ConnectivityManagerTest/assets/accesspoints.xml
new file mode 100755
index 000000000000..2b0e4af82d0d
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/assets/accesspoints.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <accesspoint>
+ <ssid>opennet</ssid>
+ <security>NONE</security>
+ </accesspoint>
+ <accesspoint>
+ <ssid>GoogleGuest</ssid>
+ <security>NONE</security>
+ </accesspoint>
+ <accesspoint>
+ <ssid>securenetdhcp</ssid>
+ <security>PSK</security>
+ <password>androidwifi</password>
+ </accesspoint>
+ <accesspoint>
+ <ssid>botnet</ssid>
+ <security>EAP</security>
+ <eap>PEAP</eap>
+ <phase2>MSCHAPV2</phase2>
+ <identity>donut</identity>
+ <password>android</password>
+ </accesspoint>
+</resources>
+
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
new file mode 100644
index 000000000000..863fbe6633db
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2010, 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 com.android.connectivitymanagertest;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.AuthAlgorithm;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+
+import android.util.Log;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Help class to process configurations of access points saved in an XML file.
+ * The configurations of an access point is included in tag
+ * <accesspoint></accesspoint>. The supported configuration includes: ssid,
+ * security, eap, phase2, identity, password, anonymousidentity, cacert, usercert,
+ * in which each is included in the corresponding tags. All access points have to be
+ * enclosed in tags of <resources></resources>.
+ *
+ * The following is a sample configuration file for an access point using EAP-PEAP with MSCHAP2.
+ * <resources>
+ * <accesspoint>
+ * <ssid>testnet</ssid>
+ * <security>EAP</security>
+ * <eap>PEAP</eap>
+ * <phase2>MSCHAP2</phase2>
+ * <identity>donut</identity</identity>
+ * <password>abcdefgh</password>
+ * </accesspoint>
+ * </resources>
+ */
+public class AccessPointParserHelper {
+ private static final String KEYSTORE_SPACE = "keystore://";
+ private static final String TAG = "AccessPointParserHelper";
+ static final int NONE = 0;
+ static final int WEP = 1;
+ static final int PSK = 2;
+ static final int EAP = 3;
+
+ List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
+
+ private int getSecurityType (String security) {
+ if (security.equalsIgnoreCase("NONE")) {
+ return NONE;
+ } else if (security.equalsIgnoreCase("WEP")) {
+ return WEP;
+ } else if (security.equalsIgnoreCase("PSK")) {
+ return PSK;
+ } else if (security.equalsIgnoreCase("EAP")) {
+ return EAP;
+ } else {
+ return -1;
+ }
+ }
+
+ private boolean validateEapValue(String value) {
+ if (value.equalsIgnoreCase("PEAP") ||
+ value.equalsIgnoreCase("TLS") ||
+ value.equalsIgnoreCase("TTLS")) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ DefaultHandler mHandler = new DefaultHandler() {
+
+ boolean ssid = false;
+ boolean security = false;
+ boolean password = false;
+ boolean ip = false;
+ boolean subnetmask = false;
+ boolean gateway = false;
+ boolean dns = false;
+ boolean eap = false;
+ boolean phase2 = false;
+ boolean identity = false;
+ boolean anonymousidentity = false;
+ boolean cacert = false;
+ boolean usercert = false;
+ WifiConfiguration config = null;
+ int securityType = NONE;
+
+ @Override
+ public void startElement(String uri, String localName, String tagName,
+ Attributes attributes) throws SAXException {
+ if (tagName.equalsIgnoreCase("accesspoint")) {
+ config = new WifiConfiguration();
+ }
+ if (tagName.equalsIgnoreCase("ssid")) {
+ ssid = true;
+ }
+ if (tagName.equalsIgnoreCase("security")) {
+ security = true;
+ }
+ if (tagName.equalsIgnoreCase("password")) {
+ password = true;
+ }
+ if (tagName.equalsIgnoreCase("eap")) {
+ eap = true;
+ }
+ if (tagName.equalsIgnoreCase("phase2")) {
+ phase2 = true;
+ }
+ if (tagName.equalsIgnoreCase("identity")) {
+ identity = true;
+ }
+ if (tagName.equalsIgnoreCase("anonymousidentity")) {
+ anonymousidentity = true;
+ }
+ if (tagName.equalsIgnoreCase("cacert")) {
+ cacert = true;
+ }
+ if (tagName.equalsIgnoreCase("usercert")) {
+ usercert = true;
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String tagName) throws SAXException {
+ Log.v(TAG, "endElement: " + tagName);
+ if (tagName.equalsIgnoreCase("accesspoint")) {
+ networks.add(config);
+ }
+ }
+
+ @Override
+ public void characters(char ch[], int start, int length) throws SAXException {
+ if (ssid) {
+ config.SSID = new String(ch, start, length);
+ Log.v(TAG, "ssid: " + config.SSID);
+ ssid = false;
+ }
+ if (security) {
+ String securityStr = (new String(ch, start, length)).toUpperCase();
+ Log.v(TAG, "security: " + securityStr);
+ securityType = getSecurityType(securityStr);
+ Log.v(TAG, "securityType = " + securityType);
+ switch (securityType) {
+ case NONE:
+ config.allowedKeyManagement.set(KeyMgmt.NONE);
+ break;
+ case WEP:
+ config.allowedKeyManagement.set(KeyMgmt.NONE);
+ config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
+ config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
+ break;
+ case PSK:
+ config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+ break;
+ case EAP:
+ config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
+ config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
+ break;
+ default:
+ throw new SAXException();
+ }
+ security = false;
+ }
+ if (password) {
+ String passwordStr = new String(ch, start, length);
+ int len = passwordStr.length();
+ if (len == 0) {
+ throw new SAXException();
+ }
+ Log.v(TAG, "passwordStr:" + passwordStr);
+ if (securityType == WEP) {
+ if ((len == 10 || len == 26 || len == 58) &&
+ passwordStr.matches("[0-9A-Fa-f]*")) {
+ config.wepKeys[0] = passwordStr;
+ } else {
+ config.wepKeys[0] = '"' + passwordStr + '"';
+ }
+ } else if (securityType == PSK) {
+ if (passwordStr.matches("[0-9A-Fa-f]{64}")) {
+ config.preSharedKey = passwordStr;
+ } else {
+ config.preSharedKey = '"' + passwordStr + '"';
+ }
+ } else if (securityType == EAP) {
+ config.password.setValue(passwordStr);
+ } else {
+ throw new SAXException();
+ }
+ password = false;
+ }
+ if (eap) {
+ String eapValue = new String(ch, start, length);
+ if (!validateEapValue(eapValue)) {
+ throw new SAXException();
+ }
+ config.eap.setValue(eapValue);
+ eap = false;
+ }
+ if (phase2) {
+ String phase2Value = new String(ch, start, length);
+ config.phase2.setValue("auth=" + phase2Value);
+ phase2 = false;
+ }
+ if (identity) {
+ String identityValue = new String(ch, start, length);
+ config.identity.setValue(identityValue);
+ identity = false;
+ }
+ if (anonymousidentity) {
+ String anonyId = new String(ch, start, length);
+ config.anonymous_identity.setValue(anonyId);
+ anonymousidentity = false;
+ }
+ if (cacert) {
+ String cacertValue = new String(ch, start, length);
+ // need to install the credentail to "keystore://"
+ config.ca_cert.setValue(KEYSTORE_SPACE);
+ cacert = false;
+ }
+ if (usercert) {
+ String usercertValue = new String(ch, start, length);
+ config.client_cert.setValue(KEYSTORE_SPACE);
+ usercert = false;
+ }
+ }
+ };
+
+ public AccessPointParserHelper() {
+ }
+
+ /**
+ * Process the accesspoint.xml file
+ * @return List of WifiConfiguration
+ * @throws Exception when parsing the XML file
+ */
+ public List<WifiConfiguration> processAccessPoint(InputStream in) throws Exception {
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ SAXParser saxParser = factory.newSAXParser();
+ saxParser.parse(in, mHandler);
+ return networks;
+ }
+}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index e42b65718e64..7c46e7a3389d 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -16,8 +16,10 @@
package com.android.connectivitymanagertest;
+import com.android.connectivitymanagertest.R;
import android.app.Activity;
import android.content.Context;
+import android.content.res.Resources;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
@@ -25,19 +27,22 @@ import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.KeyEvent;
+
+import java.io.InputStream;
+import java.util.ArrayList;
import java.util.List;
import android.widget.LinearLayout;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
+import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration.KeyMgmt;
-
/**
* An activity registered with connectivity manager broadcast
* provides network connectivity information and
@@ -46,8 +51,11 @@ import android.net.wifi.WifiConfiguration.KeyMgmt;
public class ConnectivityManagerTestActivity extends Activity {
public static final String LOG_TAG = "ConnectivityManagerTestActivity";
- public static final int WAIT_FOR_SCAN_RESULT = 5 * 1000; //5 seconds
+ public static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; //10 seconds
public static final int WIFI_SCAN_TIMEOUT = 20 * 1000;
+ public static final int SHORT_TIMEOUT = 5 * 1000;
+ public static final long LONG_TIMEOUT = 50 * 1000;
+ private static final String ACCESS_POINT_FILE = "accesspoints.xml";
public ConnectivityReceiver mConnectivityReceiver = null;
public WifiReceiver mWifiReceiver = null;
/*
@@ -175,6 +183,7 @@ public class ConnectivityManagerTestActivity extends Activity {
mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
registerReceiver(mWifiReceiver, mIntentFilter);
// Get an instance of ConnectivityManager
@@ -185,10 +194,26 @@ public class ConnectivityManagerTestActivity extends Activity {
if (mWifiManager.isWifiEnabled()) {
Log.v(LOG_TAG, "Clear Wifi before we start the test.");
- clearWifi();
+ removeConfiguredNetworksAndDisableWifi();
}
}
+ public List<WifiConfiguration> loadNetworkConfigurations() throws Exception {
+ InputStream in = getAssets().open(ACCESS_POINT_FILE);
+ AccessPointParserHelper parseHelper = new AccessPointParserHelper();
+ return parseHelper.processAccessPoint(in);
+ }
+
+ private void printNetConfig(String[] configuration) {
+ for (int i = 0; i < configuration.length; i++) {
+ if (i == 0) {
+ Log.v(LOG_TAG, "SSID: " + configuration[0]);
+ } else {
+ Log.v(LOG_TAG, " " + configuration[i]);
+ }
+ }
+ }
+
// for each network type, initialize network states to UNKNOWN, and no verification flag is set
public void initializeNetworkStates() {
for (int networkType = NUM_NETWORK_TYPES - 1; networkType >=0; networkType--) {
@@ -245,6 +270,68 @@ public class ConnectivityManagerTestActivity extends Activity {
}
}
+ // Wait for network connectivity state: CONNECTING, CONNECTED, SUSPENDED,
+ // DISCONNECTING, DISCONNECTED, UNKNOWN
+ public boolean waitForNetworkState(int networkType, State expectedState, long timeout) {
+ long startTime = System.currentTimeMillis();
+ while (true) {
+ if ((System.currentTimeMillis() - startTime) > timeout) {
+ if (mCM.getNetworkInfo(networkType).getState() != expectedState) {
+ return false;
+ } else {
+ // the broadcast has been sent out. the state has been changed.
+ Log.v(LOG_TAG, "networktype: " + networkType + " state: " +
+ mCM.getNetworkInfo(networkType));
+ return true;
+ }
+ }
+ Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType +
+ " to be " + expectedState.toString());
+ synchronized (connectivityObject) {
+ try {
+ connectivityObject.wait(SHORT_TIMEOUT);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if ((mNetworkInfo.getType() != networkType) ||
+ (mNetworkInfo.getState() != expectedState)) {
+ Log.v(LOG_TAG, "network state for " + mNetworkInfo.getType() +
+ "is: " + mNetworkInfo.getState());
+ continue;
+ }
+ return true;
+ }
+ }
+ }
+
+ // Wait for Wifi state: WIFI_STATE_DISABLED, WIFI_STATE_DISABLING, WIFI_STATE_ENABLED,
+ // WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN
+ public boolean waitForWifiState(int expectedState, long timeout) {
+ long startTime = System.currentTimeMillis();
+ while (true) {
+ if ((System.currentTimeMillis() - startTime) > timeout) {
+ if (mWifiState != expectedState) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState);
+ synchronized (wifiObject) {
+ try {
+ wifiObject.wait(SHORT_TIMEOUT);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if (mWifiState != expectedState) {
+ Log.v(LOG_TAG, "Wifi state is: " + mWifiNetworkInfo.getState());
+ continue;
+ }
+ return true;
+ }
+ }
+ }
+
// Return true if device is currently connected to mobile network
public boolean isConnectedToMobile() {
return (mNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE);
@@ -265,6 +352,22 @@ public class ConnectivityManagerTestActivity extends Activity {
* We don't verify whether the connection is successful or not, leave this to the test
*/
public boolean connectToWifi(String knownSSID) {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = knownSSID;
+ config.allowedKeyManagement.set(KeyMgmt.NONE);
+ return connectToWifiWithConfiguration(config);
+ }
+
+ /**
+ * Connect to Wi-Fi with the given configuration. Note the SSID in the configuration
+ * is pure string, we need to convert it to quoted string.
+ * @param config
+ * @return
+ */
+ public boolean connectToWifiWithConfiguration(WifiConfiguration config) {
+ String ssid = config.SSID;
+ config.SSID = convertToQuotedString(ssid);
+
//If Wifi is not enabled, enable it
if (!mWifiManager.isWifiEnabled()) {
Log.v(LOG_TAG, "Wifi is not enabled, enable it");
@@ -273,6 +376,7 @@ public class ConnectivityManagerTestActivity extends Activity {
List<ScanResult> netList = mWifiManager.getScanResults();
if (netList == null) {
+ Log.v(LOG_TAG, "scan results are null");
// if no scan results are available, start active scan
mWifiManager.startScanActive();
mScanResultIsAvailable = false;
@@ -299,17 +403,20 @@ public class ConnectivityManagerTestActivity extends Activity {
}
netList = mWifiManager.getScanResults();
+
for (int i = 0; i < netList.size(); i++) {
ScanResult sr= netList.get(i);
- if (sr.SSID.equals(knownSSID)) {
- Log.v(LOG_TAG, "found " + knownSSID + " in the scan result list");
- WifiConfiguration config = new WifiConfiguration();
- config.SSID = convertToQuotedString(sr.SSID);
- config.allowedKeyManagement.set(KeyMgmt.NONE);
+ if (sr.SSID.equals(ssid)) {
+ Log.v(LOG_TAG, "found " + ssid + " in the scan result list");
int networkId = mWifiManager.addNetwork(config);
// Connect to network by disabling others.
mWifiManager.enableNetwork(networkId, true);
mWifiManager.saveConfiguration();
+ List<WifiConfiguration> wifiNetworks = mWifiManager.getConfiguredNetworks();
+ for (WifiConfiguration netConfig : wifiNetworks) {
+ Log.v(LOG_TAG, netConfig.toString());
+ }
+
mWifiManager.reconnect();
break;
}
@@ -317,14 +424,14 @@ public class ConnectivityManagerTestActivity extends Activity {
List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks();
if (netConfList.size() <= 0) {
- Log.v(LOG_TAG, knownSSID + " is not available");
+ Log.v(LOG_TAG, ssid + " is not available");
return false;
}
return true;
}
/*
- * Disconnect from the current AP
+ * Disconnect from the current AP and remove configured networks.
*/
public boolean disconnectAP() {
if (mWifiManager.isWifiEnabled()) {
@@ -360,9 +467,9 @@ public class ConnectivityManagerTestActivity extends Activity {
}
/**
- * Disconnect from the current Wifi and clear the configuration list
+ * Remove configured networks and disable wifi
*/
- public boolean clearWifi() {
+ public boolean removeConfiguredNetworksAndDisableWifi() {
if (!disconnectAP()) {
return false;
}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
index 592be920be3d..3d4dc3d4b174 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
@@ -21,6 +21,7 @@ import android.test.InstrumentationTestRunner;
import android.test.InstrumentationTestSuite;
import android.util.Log;
import com.android.connectivitymanagertest.functional.ConnectivityManagerMobileTest;
+import com.android.connectivitymanagertest.functional.WifiConnectionTest;
import junit.framework.TestSuite;
@@ -38,6 +39,7 @@ public class ConnectivityManagerTestRunner extends InstrumentationTestRunner {
public TestSuite getAllTests() {
TestSuite suite = new InstrumentationTestSuite(this);
suite.addTestSuite(ConnectivityManagerMobileTest.class);
+ suite.addTestSuite(WifiConnectionTest.class);
return suite;
}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
index ad8d444114d7..5959cf30dd47 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
@@ -41,8 +41,6 @@ public class ConnectivityManagerMobileTest
extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> {
private static final String LOG_TAG = "ConnectivityManagerMobileTest";
private static final String PKG_NAME = "com.android.connectivitymanagertest";
- private static final long STATE_TRANSITION_SHORT_TIMEOUT = 5 * 1000;
- private static final long STATE_TRANSITION_LONG_TIMEOUT = 30 * 1000;
private String TEST_ACCESS_POINT;
private ConnectivityManagerTestActivity cmActivity;
@@ -64,9 +62,14 @@ public class ConnectivityManagerMobileTest
wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "CMWakeLock");
wl.acquire();
// Each test case will start with cellular connection
- waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
- verifyCellularConnection();
+ if (!cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT)) {
+ // Note: When the test fails in setUp(), tearDown is not called. In that case,
+ // the activity is destroyed which blocks the next test at "getActivity()".
+ // tearDown() is called hear to avoid that situation.
+ tearDown();
+ fail("Device is not connected to Mobile, setUp failed");
+ }
}
@Override
@@ -74,86 +77,26 @@ public class ConnectivityManagerMobileTest
cmActivity.finish();
Log.v(LOG_TAG, "tear down ConnectivityManagerTestActivity");
wl.release();
- cmActivity.clearWifi();
+ cmActivity.removeConfiguredNetworksAndDisableWifi();
super.tearDown();
}
// help function to verify 3G connection
public void verifyCellularConnection() {
- NetworkInfo extraNetInfo = cmActivity.mNetworkInfo;
+ NetworkInfo extraNetInfo = cmActivity.mCM.getActiveNetworkInfo();
assertEquals("network type is not MOBILE", ConnectivityManager.TYPE_MOBILE,
- extraNetInfo.getType());
+ extraNetInfo.getType());
assertTrue("not connected to cellular network", extraNetInfo.isConnected());
assertTrue("no data connection", cmActivity.mState.equals(State.CONNECTED));
}
- // Wait for network connectivity state: CONNECTING, CONNECTED, SUSPENDED,
- // DISCONNECTING, DISCONNECTED, UNKNOWN
- private void waitForNetworkState(int networkType, State expectedState, long timeout) {
- long startTime = System.currentTimeMillis();
- while (true) {
- if ((System.currentTimeMillis() - startTime) > timeout) {
- if (cmActivity.mCM.getNetworkInfo(networkType).getState() != expectedState) {
- assertFalse("Wait for network state timeout", true);
- } else {
- // the broadcast has been sent out. the state has been changed.
- return;
- }
- }
- Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType +
- " to be " + expectedState.toString());
- synchronized (cmActivity.connectivityObject) {
- try {
- cmActivity.connectivityObject.wait(STATE_TRANSITION_SHORT_TIMEOUT);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if ((cmActivity.mNetworkInfo.getType() != networkType) ||
- (cmActivity.mNetworkInfo.getState() != expectedState)) {
- Log.v(LOG_TAG, "network state for " + cmActivity.mNetworkInfo.getType() +
- "is: " + cmActivity.mNetworkInfo.getState());
- continue;
- }
- break;
- }
- }
- }
-
- // Wait for Wifi state: WIFI_STATE_DISABLED, WIFI_STATE_DISABLING, WIFI_STATE_ENABLED,
- // WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN
- private void waitForWifiState(int expectedState, long timeout) {
- long startTime = System.currentTimeMillis();
- while (true) {
- if ((System.currentTimeMillis() - startTime) > timeout) {
- if (cmActivity.mWifiState != expectedState) {
- assertFalse("Wait for Wifi state timeout", true);
- } else {
- return;
- }
- }
- Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState);
- synchronized (cmActivity.wifiObject) {
- try {
- cmActivity.wifiObject.wait(5*1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if (cmActivity.mWifiState != expectedState) {
- Log.v(LOG_TAG, "Wifi state is: " + cmActivity.mWifiNetworkInfo.getState());
- continue;
- }
- break;
- }
- }
- }
-
// Test case 1: Test enabling Wifi without associating with any AP
@LargeTest
public void test3GToWifiNotification() {
// To avoid UNKNOWN state when device boots up
cmActivity.enableWifi();
try {
- Thread.sleep(2 * STATE_TRANSITION_SHORT_TIMEOUT);
+ Thread.sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT);
} catch (Exception e) {
Log.v(LOG_TAG, "exception: " + e.toString());
}
@@ -170,7 +113,7 @@ public class ConnectivityManagerMobileTest
// Eanble Wifi
cmActivity.enableWifi();
try {
- Thread.sleep(2 * STATE_TRANSITION_SHORT_TIMEOUT);
+ Thread.sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT);
} catch (Exception e) {
Log.v(LOG_TAG, "exception: " + e.toString());
}
@@ -208,12 +151,13 @@ public class ConnectivityManagerMobileTest
assertTrue("failed to connect to " + TEST_ACCESS_POINT,
cmActivity.connectToWifi(TEST_ACCESS_POINT));
- waitForWifiState(WifiManager.WIFI_STATE_ENABLED, STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
Log.v(LOG_TAG, "wifi state is enabled");
- waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
- waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
// validate states
if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
@@ -237,12 +181,13 @@ public class ConnectivityManagerMobileTest
// Connect to TEST_ACCESS_POINT
assertTrue("failed to connect to " + TEST_ACCESS_POINT,
cmActivity.connectToWifi(TEST_ACCESS_POINT));
- waitForWifiState(WifiManager.WIFI_STATE_ENABLED, STATE_TRANSITION_LONG_TIMEOUT);
- waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
try {
- Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT);
+ Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
} catch (Exception e) {
Log.v(LOG_TAG, "exception: " + e.toString());
}
@@ -255,11 +200,12 @@ public class ConnectivityManagerMobileTest
}
// Wait for the Wifi state to be DISABLED
- waitForWifiState(WifiManager.WIFI_STATE_DISABLED, STATE_TRANSITION_LONG_TIMEOUT);
- waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
- waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_DISABLED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
//Prepare for connectivity state verification
NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
@@ -275,10 +221,10 @@ public class ConnectivityManagerMobileTest
cmActivity.enableWifi();
// Wait for Wifi to be connected and mobile to be disconnected
- waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
- waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
// validate wifi states
if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
@@ -298,12 +244,12 @@ public class ConnectivityManagerMobileTest
assertTrue("failed to connect to " + TEST_ACCESS_POINT,
cmActivity.connectToWifi(TEST_ACCESS_POINT));
- waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
// Wait for a few seconds to avoid the state that both Mobile and Wifi is connected
try {
- Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT);
+ Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
} catch (Exception e) {
Log.v(LOG_TAG, "exception: " + e.toString());
}
@@ -318,12 +264,12 @@ public class ConnectivityManagerMobileTest
NetworkState.TO_DISCONNECTION, State.DISCONNECTED);
// clear Wifi
- cmActivity.clearWifi();
+ cmActivity.removeConfiguredNetworksAndDisableWifi();
- waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
- waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
// validate states
if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
@@ -357,7 +303,7 @@ public class ConnectivityManagerMobileTest
// Enable airplane mode
cmActivity.setAirplaneMode(getInstrumentation().getContext(), true);
try {
- Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT);
+ Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
} catch (Exception e) {
Log.v(LOG_TAG, "exception: " + e.toString());
}
@@ -389,8 +335,8 @@ public class ConnectivityManagerMobileTest
// disable airplane mode
cmActivity.setAirplaneMode(getInstrumentation().getContext(), false);
- waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
// Validate the state transition
if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
@@ -414,8 +360,8 @@ public class ConnectivityManagerMobileTest
// Eanble airplane mode
cmActivity.setAirplaneMode(getInstrumentation().getContext(), true);
- waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE,
@@ -429,8 +375,8 @@ public class ConnectivityManagerMobileTest
// Connect to Wifi
assertTrue("failed to connect to " + TEST_ACCESS_POINT,
cmActivity.connectToWifi(TEST_ACCESS_POINT));
- waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
// validate state and broadcast
if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
@@ -457,11 +403,11 @@ public class ConnectivityManagerMobileTest
assertTrue("failed to connect to " + TEST_ACCESS_POINT,
cmActivity.connectToWifi(TEST_ACCESS_POINT));
- waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
try {
- Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT);
+ Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
} catch (Exception e) {
Log.v(LOG_TAG, "exception: " + e.toString());
}
@@ -469,11 +415,11 @@ public class ConnectivityManagerMobileTest
// Enable airplane mode without clearing Wifi
cmActivity.setAirplaneMode(getInstrumentation().getContext(), true);
- waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
try {
- Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT);
+ Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
} catch (Exception e) {
Log.v(LOG_TAG, "exception: " + e.toString());
}
@@ -487,10 +433,10 @@ public class ConnectivityManagerMobileTest
// Disable airplane mode
cmActivity.setAirplaneMode(getInstrumentation().getContext(), false);
- waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
- waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
// validate the state transition
if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
@@ -508,14 +454,15 @@ public class ConnectivityManagerMobileTest
//Connect to TEST_ACCESS_POINT
assertTrue("failed to connect to " + TEST_ACCESS_POINT,
cmActivity.connectToWifi(TEST_ACCESS_POINT));
- waitForWifiState(WifiManager.WIFI_STATE_ENABLED, STATE_TRANSITION_LONG_TIMEOUT);
- waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
assertNotNull("Not associated with any AP",
cmActivity.mWifiManager.getConnectionInfo().getBSSID());
try {
- Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT);
+ Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
} catch (Exception e) {
Log.v(LOG_TAG, "exception: " + e.toString());
}
@@ -527,13 +474,14 @@ public class ConnectivityManagerMobileTest
}
// Verify the connectivity state for Wifi is DISCONNECTED
- waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
- STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
if (!cmActivity.disableWifi()) {
Log.v(LOG_TAG, "disable Wifi failed");
return;
}
- waitForWifiState(WifiManager.WIFI_STATE_DISABLED, STATE_TRANSITION_LONG_TIMEOUT);
+ assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_DISABLED,
+ ConnectivityManagerTestActivity.LONG_TIMEOUT));
}
}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
new file mode 100644
index 000000000000..69eb5dbef9df
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2010, 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 com.android.connectivitymanagertest.functional;
+
+import com.android.connectivitymanagertest.ConnectivityManagerTestActivity;
+import com.android.connectivitymanagertest.NetworkState;
+
+import android.R;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
+
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test Wi-Fi connection with different configuration
+ * To run this tests:
+ * adb shell am instrument -e class
+ * com.android.connectivitymanagertest.functional.WifiConnectionTest
+ * -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner
+ */
+public class WifiConnectionTest
+ extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> {
+ private static final String TAG = "WifiConnectionTest";
+ private static final boolean DEBUG = true;
+ private static final String PKG_NAME = "com.android.connectivitymanagertests";
+ private List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
+ private ConnectivityManagerTestActivity mAct;
+
+ public WifiConnectionTest() {
+ super(PKG_NAME, ConnectivityManagerTestActivity.class);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mAct = getActivity();
+ networks = mAct.loadNetworkConfigurations();
+ if (DEBUG) {
+ printNetworkConfigurations();
+ }
+
+ // enable Wifi and verify wpa_supplicant is started
+ assertTrue("enable Wifi failed", mAct.enableWifi());
+ try {
+ Thread.sleep( 2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT);
+ } catch (Exception e) {
+ fail("interrupted while waiting for WPA_SUPPLICANT to start");
+ }
+ WifiInfo mConnection = mAct.mWifiManager.getConnectionInfo();
+ assertNotNull(mConnection);
+ assertTrue("wpa_supplicant is not started ", mAct.mWifiManager.pingSupplicant());
+ }
+
+ private void printNetworkConfigurations() {
+ Log.v(TAG, "==== print network configurations parsed from XML file ====");
+ Log.v(TAG, "number of access points: " + networks.size());
+ for (WifiConfiguration config : networks) {
+ Log.v(TAG, config.toString());
+ }
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ mAct.removeConfiguredNetworksAndDisableWifi();
+ super.tearDown();
+ }
+
+ /**
+ * Connect to the provided Wi-Fi network
+ * @param config is the network configuration
+ * @return true if the connection is successful.
+ */
+ private void connectToWifi(WifiConfiguration config) {
+ // step 1: connect to the test access point
+ assertTrue("failed to connect to " + config.SSID,
+ mAct.connectToWifiWithConfiguration(config));
+
+ // step 2: verify Wifi state and network state;
+ assertTrue(mAct.waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
+ ConnectivityManagerTestActivity.SHORT_TIMEOUT));
+ assertTrue(mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI,
+ State.CONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT));
+
+ // step 3: verify the current connected network is the given SSID
+ if (DEBUG) {
+ Log.v(TAG, "config.SSID = " + config.SSID);
+ Log.v(TAG, "mAct.mWifiManager.getConnectionInfo.getSSID()" +
+ mAct.mWifiManager.getConnectionInfo().getSSID());
+ }
+ assertTrue(config.SSID.contains(mAct.mWifiManager.getConnectionInfo().getSSID()));
+
+ // Maintain the connection for 50 seconds before switching
+ try {
+ Thread.sleep(50*1000);
+ } catch (Exception e) {
+ fail("interrupted while waiting for WPA_SUPPLICANT to start");
+ }
+ }
+
+ @LargeTest
+ public void testWifiConnections() {
+ for (int i = 0; i < networks.size(); i++) {
+ connectToWifi(networks.get(i));
+ mAct.removeConfiguredNetworksAndDisableWifi();
+ }
+ }
+}