OSDN Git Service

Add connectivity manager automation test framework:
authorXia Wang <xiaw@google.com>
Thu, 4 Mar 2010 19:54:39 +0000 (11:54 -0800)
committerXia Wang <xiaw@google.com>
Sat, 20 Mar 2010 22:29:52 +0000 (15:29 -0700)
 - Create a dummy activity - ConnectivityManagerTestActivity - to listen to broadcast from connectivity manager, to control wifi, and to verify that connectivity information for different network types. This framework will be used for funcitonal tests and stress tests.

 - Add two functional test cases:
   . test3GToWifiNotification: test enabling Wifi without associating with any access point, Wifi stays disconnected, device should still connect to cellular.
   . testConnectToWifi: when switching from cellular to Wfi and associated with a given AP, connectivity manager broadcast the right informtion for mobile and Wifi.

Change-Id: Ib6fcf0ff6dd9725076836032e567c0a6a52f622f

tests/ConnectivityManagerTest/Android.mk [new file with mode: 0644]
tests/ConnectivityManagerTest/AndroidManifest.xml [new file with mode: 0644]
tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java [new file with mode: 0644]
tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java [new file with mode: 0644]
tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java [new file with mode: 0644]
tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java [new file with mode: 0644]

diff --git a/tests/ConnectivityManagerTest/Android.mk b/tests/ConnectivityManagerTest/Android.mk
new file mode 100644 (file)
index 0000000..bd773d0
--- /dev/null
@@ -0,0 +1,30 @@
+# Copyright 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := ConnectivityManagerTest
+
+#LOCAL_INSTRUMENTATION_FOR := connectivitymanagertest
+
+include $(BUILD_PACKAGE)
diff --git a/tests/ConnectivityManagerTest/AndroidManifest.xml b/tests/ConnectivityManagerTest/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..76b58e1
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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 name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.connectivitymanagertest">
+
+    <!-- We add an application tag here just so that we can indicate that
+         this package needs to link against the android.test library,
+         which is needed when building test cases. -->
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="ConnectivityManagerTestActivity"
+          android:label="CMTest">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.TEST" />
+            </intent-filter>
+        </activity>
+    </application>
+    <!--
+    This declares that this app uses the instrumentation test runner targeting
+    the package of browserpowertest. To run the tests use the command:
+    "adb shell am instrument -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner"
+    -->
+    <instrumentation android:name=".ConnectivityManagerTestRunner"
+        android:targetPackage="com.android.connectivitymanagertest"
+        android:label="Test runner for Connectivity Manager Tests"
+    />
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+
+</manifest>
diff --git a/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
new file mode 100644 (file)
index 0000000..1dffa02
--- /dev/null
@@ -0,0 +1,329 @@
+package com.android.connectivitymanagertest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+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.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+
+
+/**
+ * An activity registered with connectivity manager broadcast
+ * provides network connectivity information and
+ * can be used to set device states: Cellular, Wifi, Airplane mode.
+ */
+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 WIFI_SCAN_TIMEOUT = 20 * 1000;
+    public ConnectivityReceiver mConnectivityReceiver = null;
+    public WifiReceiver mWifiReceiver = null;
+    /*
+     * Track network connectivity information
+     */
+    public State mState;
+    public NetworkInfo mNetworkInfo;
+    public NetworkInfo mOtherNetworkInfo;
+    public boolean mIsFailOver;
+    public String mReason;
+    public boolean mScanResultIsAvailable = false;
+    public ConnectivityManager mCM;
+
+    /*
+     * Control Wifi States
+     */
+    public WifiManager mWifiManager;
+
+    /*
+     * Verify connectivity state
+     */
+    public static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE;
+    NetworkState[] connectivityState = new NetworkState[NUM_NETWORK_TYPES];
+
+    /**
+     * A wrapper of a broadcast receiver which provides network connectivity information
+     * for all kinds of network: wifi, mobile, etc.
+     */
+    private class ConnectivityReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+                Log.v("ConnectivityReceiver", "onReceive() called with " + intent);
+                return;
+            }
+
+            boolean noConnectivity =
+                intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+
+            if (noConnectivity) {
+                mState = State.DISCONNECTED;
+            } else {
+                mState = State.CONNECTED;
+            }
+
+            mNetworkInfo = (NetworkInfo)
+                intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
+
+            mOtherNetworkInfo = (NetworkInfo)
+                intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
+
+            mReason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
+            mIsFailOver = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
+            recordNetworkState(mNetworkInfo.getType(), mNetworkInfo.getState());
+            if (mOtherNetworkInfo != null) {
+                recordNetworkState(mOtherNetworkInfo.getType(), mOtherNetworkInfo.getState());
+            }
+        }
+    }
+
+    private class WifiReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (!action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+                Log.v(LOG_TAG, "onReceive() is calleld with " + intent);
+                return;
+            }
+            notifyScanResult();
+        }
+    }
+
+    public ConnectivityManagerTestActivity() {
+        mState = State.UNKNOWN;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.v(LOG_TAG, "onCreate, inst=" + Integer.toHexString(hashCode()));
+
+        // Create a simple layout
+        LinearLayout contentView = new LinearLayout(this);
+        contentView.setOrientation(LinearLayout.VERTICAL);
+        setContentView(contentView);
+        setTitle("ConnectivityManagerTestActivity");
+
+        mConnectivityReceiver = new ConnectivityReceiver();
+        // register a connectivity receiver for CONNECTIVITY_ACTION;
+        registerReceiver(mConnectivityReceiver,
+                new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
+
+        mWifiReceiver = new WifiReceiver();
+        registerReceiver(mWifiReceiver,
+                new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+        // Get an instance of ConnectivityManager
+        mCM = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+        // Get an instance of WifiManager
+        mWifiManager =(WifiManager)getSystemService(Context.WIFI_SERVICE);
+        initializeNetworkStates();
+
+        if (mWifiManager.isWifiEnabled()) {
+            Log.v(LOG_TAG, "Clear Wifi before we start the test.");
+            clearWifi();
+        }
+     }
+
+    // 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--) {
+            connectivityState[networkType] =  new NetworkState();
+            Log.v(LOG_TAG, "Initialize network state for " + networkType + ": " +
+                    connectivityState[networkType].toString());
+        }
+    }
+
+    // deposit a network state
+    public void recordNetworkState(int networkType, State networkState) {
+        Log.v(LOG_TAG, "record network state for network " +  networkType +
+                " state is " + networkState);
+        connectivityState[networkType].recordState(networkState);
+    }
+
+    // set the state transition criteria
+    public void setStateTransitionCriteria(int networkType, State initState,
+            int transitionDir, State targetState) {
+        connectivityState[networkType].setStateTransitionCriteria(
+                initState, transitionDir, targetState);
+    }
+
+    // Validate the states recorded
+    public boolean validateNetworkStates(int networkType) {
+        Log.v(LOG_TAG, "validate network state for " + networkType + ": ");
+        return connectivityState[networkType].validateStateTransition();
+    }
+
+    // return result from network state validation
+    public String getTransitionFailureReason(int networkType) {
+        Log.v(LOG_TAG, "get network state transition failure reason for " + networkType + ": " +
+                connectivityState[networkType].toString());
+        return connectivityState[networkType].getReason();
+    }
+
+    private void notifyScanResult() {
+        synchronized (this) {
+            Log.v(LOG_TAG, "notify that scan results are available");
+            this.notify();
+        }
+    }
+
+    // Return true if device is currently connected to mobile network
+    public boolean isConnectedToMobile() {
+        return (mNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE);
+    }
+
+    // Return true if device is currently connected to Wifi
+    public boolean isConnectedToWifi() {
+        return (mNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI);
+    }
+
+    public boolean enableWifi() {
+        return mWifiManager.setWifiEnabled(true);
+    }
+
+    /**
+     * Associate the device to given SSID
+     * If the device is already associated with a WiFi, disconnect and forget it,
+     * We don't verify whether the connection is successful or not, leave this to the test
+     */
+    public boolean connectToWifi(String knownSSID) {
+        //If Wifi is not enabled, enable it
+        if (!mWifiManager.isWifiEnabled()) {
+            Log.v(LOG_TAG, "Wifi is not enabled, enable it");
+            mWifiManager.setWifiEnabled(true);
+        }
+
+        List<ScanResult> netList = mWifiManager.getScanResults();
+        if (netList == null) {
+            // if no scan results are available, start active scan
+            mWifiManager.startScanActive();
+            mScanResultIsAvailable = false;
+            long startTime = System.currentTimeMillis();
+            while (!mScanResultIsAvailable) {
+                if ((System.currentTimeMillis() - startTime) > WIFI_SCAN_TIMEOUT) {
+                    return false;
+                }
+                // wait for the scan results to be available
+                synchronized (this) {
+                    // wait for the scan result to be available
+                    try {
+                        this.wait(WAIT_FOR_SCAN_RESULT);
+                    } catch (InterruptedException e) {
+                        e.printStackTrace();
+                    }
+                    if ((mWifiManager.getScanResults() == null) ||
+                            (mWifiManager.getScanResults().size() <= 0)) {
+                        continue;
+                    }
+                    mScanResultIsAvailable = true;
+                }
+            }
+        }
+
+        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 = sr.SSID;
+                config.allowedKeyManagement.set(KeyMgmt.NONE);
+                int networkId = mWifiManager.addNetwork(config);
+                mWifiManager.saveConfiguration();
+                // Connect to network by disabling others.
+                mWifiManager.enableNetwork(networkId, true);
+                mWifiManager.reconnect();
+                break;
+           }
+        }
+
+        List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks();
+        if (netConfList.size() <= 0) {
+            Log.v(LOG_TAG, knownSSID + " is not available");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Disable Wifi
+     * @return true if Wifi is disabled successfully
+     */
+    public boolean disableWiFi() {
+        return mWifiManager.setWifiEnabled(false);
+    }
+
+    /**
+     * Disconnect from the current Wifi and clear the configuration list
+     */
+    public boolean clearWifi() {
+       if (mWifiManager.isWifiEnabled()) {
+            //remove the current network Id
+            int curNetworkId = mWifiManager.getConnectionInfo().getNetworkId();
+            mWifiManager.removeNetwork(curNetworkId);
+            mWifiManager.saveConfiguration();
+
+            // remove other saved networks
+            List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks();
+            if (netConfList != null) {
+                Log.v(LOG_TAG, "remove configured network ids");
+                for (int i = 0; i < netConfList.size(); i++) {
+                    WifiConfiguration conf = new WifiConfiguration();
+                    conf = netConfList.get(i);
+                    mWifiManager.removeNetwork(conf.networkId);
+                }
+            }
+            mWifiManager.saveConfiguration();
+            // disable Wifi
+            if (!mWifiManager.setWifiEnabled(false)) {
+                return false;
+            }
+            // wait for the actions to be completed
+            try {
+                Thread.sleep(5*1000);
+            } catch (InterruptedException e) {}
+        }
+        return true;
+    }
+
+    /**
+     * Set airplane mode
+     */
+    public void setAirplaneMode(Context context, boolean enableAM) {
+        //set the airplane mode
+        Settings.System.putInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
+                enableAM ? 1 : 0);
+        // Post the intent
+        Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        intent.putExtra("state", enableAM);
+        context.sendBroadcast(intent);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        //Unregister receiver
+        if (mConnectivityReceiver != null) {
+            unregisterReceiver(mConnectivityReceiver);
+        }
+        if (mWifiReceiver != null) {
+            unregisterReceiver(mWifiReceiver);
+        }
+        Log.v(LOG_TAG, "onDestroy, inst=" + Integer.toHexString(hashCode()));
+    }
+}
diff --git a/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
new file mode 100644 (file)
index 0000000..3affa65
--- /dev/null
@@ -0,0 +1,43 @@
+package com.android.connectivitymanagertest;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+import android.util.Log;
+import com.android.connectivitymanagertest.functional.ConnectivityManagerMobileTest;
+
+import junit.framework.TestSuite;
+
+/**
+ * Instrumentation Test Runner for all connectivity manager tests.
+ *
+ * To run the connectivity manager tests:
+ *
+ * adb shell am instrument \
+ *     -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner
+ */
+
+public class ConnectivityManagerTestRunner extends InstrumentationTestRunner {
+    @Override
+    public TestSuite getAllTests() {
+        TestSuite suite = new InstrumentationTestSuite(this);
+        suite.addTestSuite(ConnectivityManagerMobileTest.class);
+        return suite;
+    }
+
+    @Override
+    public ClassLoader getLoader() {
+        return ConnectivityManagerTestRunner.class.getClassLoader();
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        String testSSID = (String) icicle.get("ssid");
+        if (testSSID != null) {
+            TEST_SSID = testSSID;
+        }
+    }
+
+    public String TEST_SSID = "GoogleGuest";
+}
diff --git a/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java
new file mode 100644 (file)
index 0000000..925120e
--- /dev/null
@@ -0,0 +1,177 @@
+package com.android.connectivitymanagertest;
+
+import android.net.NetworkInfo.State;
+import android.util.Log;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class NetworkState {
+    public static final int TO_DISCONNECTION = 0; // transition to disconnection
+    public static final int TO_CONNECTION = 1; // transition to connection
+    public static final int DO_NOTHING = -1;   // no state change
+    private final String LOG_TAG = "NetworkState";
+    private List<State> mStateDepository;
+    private State mTransitionTarget;
+    private int mTransitionDirection;
+    private String mReason = null;         // record mReason of state transition failure
+
+    public NetworkState() {
+        mStateDepository = new ArrayList<State>();
+        mTransitionDirection = DO_NOTHING;
+        mTransitionTarget = State.UNKNOWN;
+    }
+
+    public NetworkState(State currentState) {
+        mStateDepository = new ArrayList<State>();
+        mStateDepository.add(currentState);
+        mTransitionDirection = DO_NOTHING;
+        mTransitionTarget = State.UNKNOWN;
+    }
+
+    // Reinitialize the network state
+    public void resetNetworkState() {
+        mStateDepository.clear();
+        mTransitionDirection = DO_NOTHING;
+        mTransitionTarget = State.UNKNOWN;
+    }
+
+    // set the transition criteria, transitionDir could be:
+    // DO_NOTHING, TO_CONNECTION, TO_DISCONNECTION
+    public void setStateTransitionCriteria(State initState, int transitionDir, State targetState) {
+        if (!mStateDepository.isEmpty()) {
+            mStateDepository.clear();
+        }
+        mStateDepository.add(initState);
+        mTransitionDirection = transitionDir;
+        mTransitionTarget = targetState;
+        Log.v(LOG_TAG, "setStateTransitionCriteria: " + printStates());
+    }
+
+    public void recordState(State currentState) {
+        mStateDepository.add(currentState);
+    }
+
+    // Verify state transition
+    public boolean validateStateTransition() {
+        Log.v(LOG_TAG, "print state depository: " + printStates());
+        if (mTransitionDirection == DO_NOTHING) {
+            if (mStateDepository.isEmpty()) {
+                Log.v(LOG_TAG, "no state is recorded");
+                mReason = "no state is recorded.";
+                return false;
+            } else if (mStateDepository.size() > 1) {
+                Log.v(LOG_TAG, "no broadcast is expected, " +
+                        "instead broadcast is probably received");
+                mReason = "no broadcast is expected, instead broadcast is probably received";
+                return false;
+            } else if (mStateDepository.get(0) != mTransitionTarget) {
+                Log.v(LOG_TAG, mTransitionTarget + " is expected, but it is " +
+                        mStateDepository.get(0));
+                mReason = mTransitionTarget + " is expected, but it is " + mStateDepository.get(0);
+                return false;
+            }
+            return true;
+        } else if (mTransitionDirection == TO_CONNECTION) {
+            Log.v(LOG_TAG, "transition to CONNECTED");
+            return transitToConnection();
+        } else {
+            Log.v(LOG_TAG, "transition to DISCONNECTED");
+            return transitToDisconnection();
+        }
+    }
+
+    /*
+     * Transition from CONNECTED -> DISCONNECTED:
+     *    CONNECTED->DISCONNECTING->DISCONNECTED
+     * return false if any state transition is not valid and save a message in mReason
+     */
+    public boolean transitToDisconnection () {
+        mReason = "states: " + printStates();
+        if (mStateDepository.get(0) != State.CONNECTED) {
+            mReason += " initial state should be CONNECTED, but it is " +
+                    mStateDepository.get(0) + ".";
+            return false;
+        }
+        State lastState = mStateDepository.get(mStateDepository.size() - 1);
+        if ( lastState != mTransitionTarget) {
+            mReason += " the last state should be DISCONNECTED, but it is " + lastState;
+            return false;
+        }
+        for (int i = 1; i < mStateDepository.size() - 1; i++) {
+            State preState = mStateDepository.get(i-1);
+            State curState = mStateDepository.get(i);
+            if ((preState == State.CONNECTED) && ((curState == State.DISCONNECTING) ||
+                    (curState == State.DISCONNECTED))) {
+                continue;
+            } else if ((preState == State.DISCONNECTING) && (curState == State.DISCONNECTED)) {
+                continue;
+            } else if ((preState == State.DISCONNECTED) && (curState == State.DISCONNECTED)) {
+                continue;
+            } else {
+                mReason += " Transition state from " + preState.toString() + " to " +
+                        curState.toString() + " is not valid.";
+                return false;
+            }
+        }
+        return true;
+    }
+
+    // DISCONNECTED->CONNECTING->CONNECTED
+    public boolean transitToConnection() {
+        mReason = "states: " + printStates();
+        if (mStateDepository.get(0) != State.DISCONNECTED) {
+            mReason += " initial state should be DISCONNECTED, but it is " +
+                    mStateDepository.get(0) + ".";
+            return false;
+        }
+        State lastState = mStateDepository.get(mStateDepository.size() - 1);
+        if ( lastState != mTransitionTarget) {
+            mReason += " the last state should be CONNECTED, but it is " + lastState;
+            return false;
+        }
+        for (int i = 1; i < mStateDepository.size(); i++) {
+            State preState = mStateDepository.get(i-1);
+            State curState = mStateDepository.get(i);
+            if ((preState == State.DISCONNECTED) && ((curState == State.CONNECTING) ||
+                    (curState == State.CONNECTED))) {
+                continue;
+            } else if ((preState == State.CONNECTING) && (curState == State.CONNECTED)) {
+                continue;
+            } else if ((preState == State.CONNECTED) && (curState == State.CONNECTED)) {
+                continue;
+            } else {
+                mReason += " Transition state from " + preState.toString() + " to " +
+                        curState.toString() + " is not valid.";
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public List<State> getTransitionStates() {
+        return mStateDepository;
+    }
+
+    // return state failure mReason
+    public String getReason() {
+        return mReason;
+    }
+
+    public String printStates() {
+        StringBuilder stateBuilder = new StringBuilder("");
+        for (int i = 0; i < mStateDepository.size(); i++) {
+            stateBuilder.append(" ").append(mStateDepository.get(i).toString()).append("->");
+        }
+        return stateBuilder.toString();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder(" ");
+        builder.append("mTransitionDirection: ").append(Integer.toString(mTransitionDirection)).
+                append("; ").append("states:").
+                append(printStates()).append("; ");
+        return builder.toString();
+    }
+}
diff --git a/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
new file mode 100644 (file)
index 0000000..ab81bb8
--- /dev/null
@@ -0,0 +1,135 @@
+package com.android.connectivitymanagertest.functional;
+
+import com.android.connectivitymanagertest.ConnectivityManagerTestActivity;
+
+import android.content.Intent;
+import android.content.Context;
+import android.app.Instrumentation;
+import android.os.Handler;
+import android.os.Message;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
+import android.net.NetworkInfo.DetailedState;
+
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.ActivityInstrumentationTestCase2;
+import com.android.connectivitymanagertest.ConnectivityManagerTestRunner;
+import com.android.connectivitymanagertest.NetworkState;
+import android.util.Log;
+import junit.framework.*;
+
+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 WIFI_CONNECTION_TIMEOUT = 30 * 1000;
+    private static final long WIFI_NOTIFICATION_TIMEOUT = 10 * 1000;
+    private String TEST_ACCESS_POINT;
+    private ConnectivityManagerTestActivity cmActivity;
+
+    public ConnectivityManagerMobileTest() {
+        super(PKG_NAME, ConnectivityManagerTestActivity.class);
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        cmActivity = getActivity();
+        ConnectivityManagerTestRunner mRunner =
+                (ConnectivityManagerTestRunner)getInstrumentation();
+        TEST_ACCESS_POINT = mRunner.TEST_SSID;
+        // Each test case will start with cellular connection
+        verifyCellularConnection();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        // clear Wifi after each test case
+        cmActivity.clearWifi();
+        cmActivity.finish();
+        Log.v(LOG_TAG, "tear down ConnectivityManager test activity");
+        super.tearDown();
+    }
+
+    // help function to verify 3G connection
+    public void verifyCellularConnection() {
+        NetworkInfo extraNetInfo = cmActivity.mNetworkInfo;
+        assertEquals("network type is not MOBILE", ConnectivityManager.TYPE_MOBILE,
+            extraNetInfo.getType());
+        assertTrue("not connected to cellular network", extraNetInfo.isConnected());
+        assertTrue("no data connection", cmActivity.mState.equals(State.CONNECTED));
+    }
+
+    // Test case 1: Test enabling Wifi without associating with any AP
+    @LargeTest
+    public void test3GToWifiNotification() {
+        // As Wifi stays in DISCONNECTED, the connectivity manager will not broadcast
+        // any network connectivity event for Wifi
+        NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+        cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(),
+                NetworkState.DO_NOTHING, State.CONNECTED);
+        networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+        cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(),
+                NetworkState.DO_NOTHING, State.DISCONNECTED);
+        // Eanble Wifi
+        cmActivity.enableWifi();
+        try {
+            Thread.sleep(WIFI_NOTIFICATION_TIMEOUT);
+        } catch (Exception e) {
+            Log.v(LOG_TAG, "exception: " + e.toString());
+        }
+
+        // validate state and broadcast
+        if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
+            Log.v(LOG_TAG, "the state for WIFI is changed");
+            Log.v(LOG_TAG, "reason: " +
+                    cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
+            assertTrue(false);
+        }
+        if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
+            Log.v(LOG_TAG, "the state for MOBILE is changed");
+            Log.v(LOG_TAG, "reason: " +
+                    cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
+            assertTrue(false);
+        }
+        // Verify that the device is still connected to MOBILE
+        verifyCellularConnection();
+    }
+
+    // Test case 2: test connection to a given AP
+    @LargeTest
+    public void testConnectToWifi() {
+        //Prepare for connectivity verification
+        NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+        cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(),
+                NetworkState.TO_DISCONNECTION, State.DISCONNECTED);
+        networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+        cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(),
+                NetworkState.TO_CONNECTION, State.CONNECTED);
+
+        // Enable Wifi and connect to a test access point
+        assertTrue("failed to connect to " + TEST_ACCESS_POINT,
+                cmActivity.connectToWifi(TEST_ACCESS_POINT));
+        try {
+            Thread.sleep(WIFI_CONNECTION_TIMEOUT);
+        } catch (Exception e) {
+            Log.v(LOG_TAG, "exception: " + e.toString());
+        }
+
+        // validate states
+        if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
+            Log.v(LOG_TAG, "Wifi state transition validation failed.");
+            Log.v(LOG_TAG, "reason: " +
+                    cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
+            assertTrue(false);
+        }
+        if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
+            Log.v(LOG_TAG, "Mobile state transition validation failed.");
+            Log.v(LOG_TAG, "reason: " +
+                    cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
+            assertTrue(false);
+        }
+    }
+}