1/*
2 * Copyright (C) 2010, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.connectivitymanagertest;
18
19import android.app.KeyguardManager;
20import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.net.ConnectivityManager;
25import android.net.NetworkInfo;
26import android.net.NetworkInfo.State;
27import android.net.wifi.ScanResult;
28import android.net.wifi.WifiConfiguration;
29import android.net.wifi.WifiManager;
30import android.os.PowerManager;
31import android.os.SystemClock;
32import android.test.InstrumentationTestCase;
33import android.util.Log;
34import android.view.KeyEvent;
35
36import java.io.IOException;
37import java.net.UnknownHostException;
38import java.util.List;
39
40
41/**
42 * Base InstrumentationTestCase for Connectivity Manager (CM) test suite
43 *
44 * It registers connectivity manager broadcast and WiFi broadcast to provide
45 * network connectivity information, also provides a set of utility functions
46 * to modify and verify connectivity states.
47 *
48 * A CM test case should extend this base class.
49 */
50public class ConnectivityManagerTestBase extends InstrumentationTestCase {
51
52    private static final String[] PING_HOST_LIST = {
53        "www.google.com", "www.yahoo.com", "www.bing.com", "www.facebook.com", "www.ask.com"};
54
55    protected static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; //10 seconds
56    protected static final int WIFI_SCAN_TIMEOUT = 50 * 1000; // 50 seconds
57    protected static final int SHORT_TIMEOUT = 5 * 1000; // 5 seconds
58    protected static final long LONG_TIMEOUT = 2 * 60 * 1000;  // 2 minutes
59    protected static final long WIFI_CONNECTION_TIMEOUT = 5 * 60 * 1000; // 5 minutes
60    // 2 minutes timer between wifi stop and start
61    protected static final long  WIFI_STOP_START_INTERVAL = 2 * 60 * 1000; // 2 minutes
62    // Set ping test timer to be 3 minutes
63    protected static final long PING_TIMER = 3 * 60 *1000; // 3 minutes
64    protected static final int SUCCESS = 0;  // for Wifi tethering state change
65    protected static final int FAILURE = 1;
66    protected static final int INIT = -1;
67
68    protected final String mLogTag;
69
70    private ConnectivityReceiver mConnectivityReceiver = null;
71    private WifiReceiver mWifiReceiver = null;
72
73    private long mLastConnectivityChangeTime = -1;
74    protected ConnectivityManager mCm;
75    private Context mContext;
76    protected List<ScanResult> mLastScanResult;
77    protected Object mWifiScanResultLock = new Object();
78
79    /* Control Wifi States */
80    public WifiManager mWifiManager;
81
82    public ConnectivityManagerTestBase(String logTag) {
83        super();
84        mLogTag = logTag;
85    }
86
87    protected long getLastConnectivityChangeTime() {
88        return mLastConnectivityChangeTime;
89    }
90
91    /**
92     * A wrapper of a broadcast receiver which provides network connectivity information
93     * for all kinds of network: wifi, mobile, etc.
94     */
95    private class ConnectivityReceiver extends BroadcastReceiver {
96        @Override
97        public void onReceive(Context context, Intent intent) {
98            mLastConnectivityChangeTime = SystemClock.uptimeMillis();
99            logv("ConnectivityReceiver: " + intent);
100            String action = intent.getAction();
101            if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
102                Log.v("ConnectivityReceiver", "onReceive() called with " + intent);
103            }
104        }
105    }
106
107    private class WifiReceiver extends BroadcastReceiver {
108        @Override
109        public void onReceive(Context context, Intent intent) {
110            String action = intent.getAction();
111            Log.v("WifiReceiver", "onReceive() is calleld with " + intent);
112            if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
113                logv("scan results are available");
114                synchronized (mWifiScanResultLock) {
115                    mLastScanResult = mWifiManager.getScanResults();
116                    mWifiScanResultLock.notifyAll();
117                }
118            }
119        }
120    }
121
122    @Override
123    protected void setUp() throws Exception {
124        mLastScanResult = null;
125        mContext = getInstrumentation().getContext();
126
127        // Get an instance of ConnectivityManager
128        mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
129        // Get an instance of WifiManager
130        mWifiManager =(WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
131
132        if (mWifiManager.isWifiApEnabled()) {
133            // if soft AP is enabled, disable it
134            mWifiManager.setWifiApEnabled(null, false);
135            logv("Disable soft ap");
136        }
137
138        // register a connectivity receiver for CONNECTIVITY_ACTION;
139        mConnectivityReceiver = new ConnectivityReceiver();
140        mContext.registerReceiver(mConnectivityReceiver,
141                new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
142
143        mWifiReceiver = new WifiReceiver();
144        IntentFilter mIntentFilter = new IntentFilter();
145        mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
146        mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
147        mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
148        mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
149        mIntentFilter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
150        mIntentFilter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
151        mContext.registerReceiver(mWifiReceiver, mIntentFilter);
152
153        logv("Clear Wifi before we start the test.");
154        removeConfiguredNetworksAndDisableWifi();
155     }
156
157    // wait for network connectivity state: CONNECTING, CONNECTED, SUSPENDED, DISCONNECTING,
158    //                                      DISCONNECTED, UNKNOWN
159    protected boolean waitForNetworkState(int networkType, State expectedState, long timeout) {
160        long startTime = SystemClock.uptimeMillis();
161        while (true) {
162            NetworkInfo ni = mCm.getNetworkInfo(networkType);
163            if (ni != null && expectedState.equals(ni.getState())) {
164                logv("waitForNetworkState success: %s", ni);
165                return true;
166            }
167            if ((SystemClock.uptimeMillis() - startTime) > timeout) {
168                logv("waitForNetworkState timeout: %s", ni);
169                return false;
170            }
171            logv("waitForNetworkState interim: %s", ni);
172            SystemClock.sleep(SHORT_TIMEOUT);
173        }
174    }
175
176    // wait for Wifi state: WIFI_STATE_DISABLED, WIFI_STATE_DISABLING, WIFI_STATE_ENABLED,
177    //                      WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN
178    protected boolean waitForWifiState(int expectedState, long timeout) {
179        long startTime = SystemClock.uptimeMillis();
180        while (true) {
181            int state = mWifiManager.getWifiState();
182            if (state == expectedState) {
183                logv("waitForWifiState success: state=" + state);
184                return true;
185            }
186            if ((SystemClock.uptimeMillis() - startTime) > timeout) {
187                logv("waitForWifiState timeout: expected=%d, actual=%d", expectedState, state);
188                return false;
189            }
190            logv("waitForWifiState interim: expected=%d, actual=%d", expectedState, state);
191            SystemClock.sleep(SHORT_TIMEOUT);
192        }
193    }
194
195    // Wait for Wifi AP state: WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING,
196    //                         WIFI_AP_STATE_ENABLED, WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN
197    protected boolean waitForWifiApState(int expectedState, long timeout) {
198        long startTime = SystemClock.uptimeMillis();
199        while (true) {
200            int state = mWifiManager.getWifiApState();
201            if (state == expectedState) {
202                logv("waitForWifiAPState success: state=" + state);
203                return true;
204            }
205            if ((SystemClock.uptimeMillis() - startTime) > timeout) {
206                logv(String.format("waitForWifiAPState timeout: expected=%d, actual=%d",
207                        expectedState, state));
208                return false;
209            }
210            logv(String.format("waitForWifiAPState interim: expected=%d, actual=%d",
211                    expectedState, state));
212            SystemClock.sleep(SHORT_TIMEOUT);
213        }
214    }
215
216    /**
217     * Wait for the wifi tethering result:
218     * @param timeout is the maximum waiting time
219     * @return SUCCESS if tethering result is successful
220     *         FAILURE if tethering result returns error.
221     */
222    protected boolean waitForTetherStateChange(long timeout) {
223        long startTime = SystemClock.uptimeMillis();
224        String[] wifiRegexes = mCm.getTetherableWifiRegexs();
225        while (true) {
226            if ((SystemClock.uptimeMillis() - startTime) > timeout) {
227                return false;
228            }
229            String[] active = mCm.getTetheredIfaces();
230            String[] error = mCm.getTetheringErroredIfaces();
231            for (String iface: active) {
232                for (String regex: wifiRegexes) {
233                    if (iface.matches(regex)) {
234                        return true;
235                    }
236                }
237            }
238            for (String iface: error) {
239                for (String regex: wifiRegexes) {
240                    if (iface.matches(regex)) {
241                        return false;
242                    }
243                }
244            }
245            SystemClock.sleep(SHORT_TIMEOUT);
246        }
247    }
248
249    // Return true if device is currently connected to mobile network
250    protected boolean isConnectedToMobile() {
251        return (mCm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_MOBILE);
252    }
253
254    // Return true if device is currently connected to Wifi
255    protected boolean isConnectedToWifi() {
256        return (mCm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI);
257    }
258
259    protected boolean enableWifi() {
260        return mWifiManager.setWifiEnabled(true);
261    }
262
263    // Turn screen off
264    protected void turnScreenOff() {
265        logv("Turn screen off");
266        PowerManager pm =
267            (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
268        pm.goToSleep(SystemClock.uptimeMillis());
269    }
270
271    // Turn screen on
272    protected void turnScreenOn() {
273        logv("Turn screen on");
274        PowerManager pm =
275                (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
276        pm.wakeUp(SystemClock.uptimeMillis());
277        // disable lock screen
278        KeyguardManager km = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
279        if (km.inKeyguardRestrictedInputMode()) {
280            sendKeys(KeyEvent.KEYCODE_MENU);
281        }
282    }
283
284    /**
285     * @return true if the ping test is successful, false otherwise.
286     */
287    protected boolean pingTest() {
288        long startTime = System.currentTimeMillis();
289        while ((System.currentTimeMillis() - startTime) < PING_TIMER) {
290            try {
291                // assume the chance that all servers are down is very small
292                for (String host : PING_HOST_LIST) {
293                    logv("Start ping test, ping " + host);
294                    Process p = Runtime.getRuntime().exec("ping -c 10 -w 100 " + host);
295                    int status = p.waitFor();
296                    if (status == 0) {
297                        // if any of the ping test is successful, return true
298                        return true;
299                    }
300                }
301            } catch (UnknownHostException e) {
302                logv("Ping test Fail: Unknown Host");
303            } catch (IOException e) {
304                logv("Ping test Fail:  IOException");
305            } catch (InterruptedException e) {
306                logv("Ping test Fail: InterruptedException");
307            }
308            SystemClock.sleep(SHORT_TIMEOUT);
309        }
310        // ping test timeout
311        return false;
312    }
313
314    /**
315     * Associate the device to given SSID
316     * If the device is already associated with a WiFi, disconnect and forget it,
317     * We don't verify whether the connection is successful or not, leave this to the test
318     */
319    protected boolean connectToWifi(String ssid, String password) {
320        WifiConfiguration config;
321        if (password == null) {
322            config = WifiConfigurationHelper.createOpenConfig(ssid);
323        } else {
324            config = WifiConfigurationHelper.createPskConfig(ssid, password);
325        }
326        return connectToWifiWithConfiguration(config);
327    }
328
329    /**
330     * Connect to Wi-Fi with the given configuration. Note the SSID in the configuration
331     * is pure string, we need to convert it to quoted string.
332     */
333    protected boolean connectToWifiWithConfiguration(WifiConfiguration config) {
334        // If Wifi is not enabled, enable it
335        if (!mWifiManager.isWifiEnabled()) {
336            logv("Wifi is not enabled, enable it");
337            mWifiManager.setWifiEnabled(true);
338            // wait for the wifi state change before start scanning.
339            if (!waitForWifiState(WifiManager.WIFI_STATE_ENABLED, LONG_TIMEOUT)) {
340                logv("wait for WIFI_STATE_ENABLED failed");
341                return false;
342            }
343        }
344
345        // Save network configuration and connect to network without scanning
346        mWifiManager.connect(config,
347            new WifiManager.ActionListener() {
348                @Override
349                public void onSuccess() {
350                }
351
352                @Override
353                public void onFailure(int reason) {
354                    logv("connect failure " + reason);
355                }
356            });
357        return true;
358    }
359
360    /*
361     * Disconnect from the current AP and remove configured networks.
362     */
363    protected boolean disconnectAP() {
364        // remove saved networks
365        if (!mWifiManager.isWifiEnabled()) {
366            logv("Enabled wifi before remove configured networks");
367            mWifiManager.setWifiEnabled(true);
368            SystemClock.sleep(SHORT_TIMEOUT);
369        }
370
371        List<WifiConfiguration> wifiConfigList = mWifiManager.getConfiguredNetworks();
372        if (wifiConfigList == null) {
373            logv("no configuration list is null");
374            return true;
375        }
376        logv("size of wifiConfigList: " + wifiConfigList.size());
377        for (WifiConfiguration wifiConfig: wifiConfigList) {
378            logv("remove wifi configuration: " + wifiConfig.networkId);
379            int netId = wifiConfig.networkId;
380            mWifiManager.forget(netId, new WifiManager.ActionListener() {
381                    @Override
382                    public void onSuccess() {
383                    }
384
385                    @Override
386                    public void onFailure(int reason) {
387                        logv("Failed to forget " + reason);
388                    }
389                });
390        }
391        return true;
392    }
393    /**
394     * Disable Wifi
395     * @return true if Wifi is disabled successfully
396     */
397    protected boolean disableWifi() {
398        return mWifiManager.setWifiEnabled(false);
399    }
400
401    /**
402     * Remove configured networks and disable wifi
403     */
404    protected boolean removeConfiguredNetworksAndDisableWifi() {
405        if (!disconnectAP()) {
406           return false;
407        }
408        SystemClock.sleep(SHORT_TIMEOUT);
409        if (!mWifiManager.setWifiEnabled(false)) {
410            return false;
411        }
412        SystemClock.sleep(SHORT_TIMEOUT);
413        return true;
414    }
415
416    protected static String convertToQuotedString(String string) {
417        return "\"" + string + "\"";
418    }
419
420    protected boolean waitForActiveNetworkConnection(long timeout) {
421        long startTime = SystemClock.uptimeMillis();
422        while (true) {
423            NetworkInfo ni = mCm.getActiveNetworkInfo();
424            if (ni != null && ni.isConnected()) {
425                return true;
426            }
427            if ((SystemClock.uptimeMillis() - startTime) > timeout) {
428                logv("waitForActiveNetworkConnection timeout: %s", ni);
429                return false;
430            }
431            logv("waitForActiveNetworkConnection interim: %s", ni);
432            SystemClock.sleep(SHORT_TIMEOUT);
433        }
434    }
435
436    protected boolean waitUntilNoActiveNetworkConnection(long timeout) {
437        long startTime = SystemClock.uptimeMillis();
438        while (true) {
439            NetworkInfo ni = mCm.getActiveNetworkInfo();
440            if (ni == null) {
441                return true;
442            }
443            if ((SystemClock.uptimeMillis() - startTime) > timeout) {
444                logv("waitForActiveNetworkConnection timeout: %s", ni);
445                return false;
446            }
447            logv("waitForActiveNetworkConnection interim: %s", ni);
448            SystemClock.sleep(SHORT_TIMEOUT);
449        }
450    }
451
452    // use ping request against Google public DNS to verify connectivity
453    protected boolean checkNetworkConnectivity() {
454        assertTrue("no active network connection", waitForActiveNetworkConnection(LONG_TIMEOUT));
455        return pingTest();
456    }
457
458    @Override
459    protected void tearDown() throws Exception{
460        //Unregister receiver
461        if (mConnectivityReceiver != null) {
462          mContext.unregisterReceiver(mConnectivityReceiver);
463        }
464        if (mWifiReceiver != null) {
465          mContext.unregisterReceiver(mWifiReceiver);
466        }
467        super.tearDown();
468    }
469
470    protected void logv(String format, Object... args) {
471        Log.v(mLogTag, String.format(format, args));
472    }
473
474    /**
475     * Connect to the provided Wi-Fi network
476     * @param config is the network configuration
477     * @throws AssertionError if fails to associate and connect to wifi ap
478     */
479    protected void connectToWifi(WifiConfiguration config) {
480        // step 1: connect to the test access point
481        assertTrue("failed to associate with " + config.SSID,
482                connectToWifiWithConfiguration(config));
483
484        // step 2: verify Wifi state and network state;
485        assertTrue("wifi state not connected with " + config.SSID,
486                waitForNetworkState(ConnectivityManager.TYPE_WIFI,
487                State.CONNECTED, WIFI_CONNECTION_TIMEOUT));
488
489        // step 3: verify the current connected network is the given SSID
490        assertNotNull("no active wifi info", mWifiManager.getConnectionInfo());
491        assertEquals("SSID mismatch", config.SSID, mWifiManager.getConnectionInfo().getSSID());
492    }
493}
494