1/*
2 * Copyright (C) 2011, 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.bandwidthtest.util;
18
19import android.app.DownloadManager;
20import android.app.DownloadManager.Query;
21import android.app.DownloadManager.Request;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.ApplicationInfo;
27import android.content.pm.PackageManager;
28import android.content.pm.PackageManager.NameNotFoundException;
29import android.database.Cursor;
30import android.net.ConnectivityManager;
31import android.net.NetworkInfo;
32import android.net.NetworkInfo.State;
33import android.net.Uri;
34import android.net.wifi.ScanResult;
35import android.net.wifi.WifiConfiguration;
36import android.net.wifi.WifiConfiguration.KeyMgmt;
37import android.net.wifi.WifiManager;
38import android.os.Handler;
39import android.os.Message;
40import android.provider.Settings;
41import android.util.Log;
42
43import com.android.bandwidthtest.NetworkState;
44import com.android.bandwidthtest.NetworkState.StateTransitionDirection;
45import com.android.internal.util.AsyncChannel;
46
47import junit.framework.Assert;
48
49import java.io.IOException;
50import java.net.UnknownHostException;
51import java.util.List;
52
53/*
54 * Utility class used to set the connectivity of the device and to download files.
55 */
56public class ConnectionUtil {
57    private static final String LOG_TAG = "ConnectionUtil";
58    private static final String DOWNLOAD_MANAGER_PKG_NAME = "com.android.providers.downloads";
59    private static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; // 10 seconds
60    private static final int WIFI_SCAN_TIMEOUT = 50 * 1000;
61    public static final int SHORT_TIMEOUT = 5 * 1000;
62    public static final int LONG_TIMEOUT = 5 * 60 * 1000; // 5 minutes
63    private ConnectivityReceiver mConnectivityReceiver = null;
64    private WifiReceiver mWifiReceiver = null;
65    private DownloadReceiver mDownloadReceiver = null;
66    private DownloadManager mDownloadManager;
67    private NetworkInfo mNetworkInfo;
68    private NetworkInfo mOtherNetworkInfo;
69    private boolean mScanResultIsAvailable = false;
70    private ConnectivityManager mCM;
71    private Object mWifiMonitor = new Object();
72    private Object mConnectivityMonitor = new Object();
73    private Object mDownloadMonitor = new Object();
74    private int mWifiState;
75    private NetworkInfo mWifiNetworkInfo;
76    private WifiManager mWifiManager;
77    private Context mContext;
78    // Verify connectivity state
79    private static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE + 1;
80    private NetworkState[] mConnectivityState = new NetworkState[NUM_NETWORK_TYPES];
81
82    public ConnectionUtil(Context context) {
83        mContext = context;
84    }
85
86    /**
87     * Initialize the class. Needs to be called before any other methods in {@link ConnectionUtil}
88     *
89     * @throws Exception
90     */
91    public void initialize() throws Exception {
92        // Register a connectivity receiver for CONNECTIVITY_ACTION
93        mConnectivityReceiver = new ConnectivityReceiver();
94        mContext.registerReceiver(mConnectivityReceiver,
95                new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
96
97        // Register a download receiver for ACTION_DOWNLOAD_COMPLETE
98        mDownloadReceiver = new DownloadReceiver();
99        mContext.registerReceiver(mDownloadReceiver,
100                new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
101
102        // Register a wifi receiver
103        mWifiReceiver = new WifiReceiver();
104        IntentFilter mIntentFilter = new IntentFilter();
105        mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
106        mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
107        mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
108        mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
109        mIntentFilter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
110        mContext.registerReceiver(mWifiReceiver, mIntentFilter);
111
112        // Get an instance of ConnectivityManager
113        mCM = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
114
115        // Get an instance of WifiManager
116        mWifiManager =(WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
117
118        mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE);
119
120        initializeNetworkStates();
121
122
123    }
124
125    /**
126     * Additional initialization needed for wifi related tests.
127     */
128    public void wifiTestInit() {
129        mWifiManager.setWifiEnabled(true);
130        Log.v(LOG_TAG, "Clear Wifi before we start the test.");
131        sleep(SHORT_TIMEOUT);
132        removeConfiguredNetworksAndDisableWifi();
133    }
134
135
136    /**
137     * A wrapper of a broadcast receiver which provides network connectivity information
138     * for all kinds of network: wifi, mobile, etc.
139     */
140    private class ConnectivityReceiver extends BroadcastReceiver {
141        /**
142         * {@inheritDoc}
143         */
144        @Override
145        public void onReceive(Context context, Intent intent) {
146            if (isInitialStickyBroadcast()) {
147                Log.d(LOG_TAG, "This is a sticky broadcast don't do anything.");
148                return;
149            }
150            Log.v(LOG_TAG, "ConnectivityReceiver: onReceive() is called with " + intent);
151            String action = intent.getAction();
152            if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
153                Log.v("ConnectivityReceiver", "onReceive() called with " + intent);
154                return;
155            }
156
157            final ConnectivityManager connManager = (ConnectivityManager) context
158                    .getSystemService(Context.CONNECTIVITY_SERVICE);
159            mNetworkInfo = connManager.getActiveNetworkInfo();
160
161            if (intent.hasExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO)) {
162                mOtherNetworkInfo = (NetworkInfo)
163                        intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
164            }
165
166            Log.v(LOG_TAG, "mNetworkInfo: " + mNetworkInfo.toString());
167            recordNetworkState(mNetworkInfo.getType(), mNetworkInfo.getState());
168            if (mOtherNetworkInfo != null) {
169                Log.v(LOG_TAG, "mOtherNetworkInfo: " + mOtherNetworkInfo.toString());
170                recordNetworkState(mOtherNetworkInfo.getType(), mOtherNetworkInfo.getState());
171            }
172            notifyNetworkConnectivityChange();
173        }
174    }
175
176    /**
177     * A wrapper of a broadcast receiver which provides wifi information.
178     */
179    private class WifiReceiver extends BroadcastReceiver {
180        /**
181         * {@inheritDoc}
182         */
183        @Override
184        public void onReceive(Context context, Intent intent) {
185            String action = intent.getAction();
186            Log.v("WifiReceiver", "onReceive() is calleld with " + intent);
187            if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
188                Log.v(LOG_TAG, "Scan results are available");
189                notifyScanResult();
190            } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
191                mWifiNetworkInfo =
192                        (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
193                Log.v(LOG_TAG, "mWifiNetworkInfo: " + mWifiNetworkInfo.toString());
194                if (mWifiNetworkInfo.getState() == State.CONNECTED) {
195                    intent.getStringExtra(WifiManager.EXTRA_BSSID);
196                }
197                notifyWifiState();
198            } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
199                mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
200                        WifiManager.WIFI_STATE_UNKNOWN);
201                notifyWifiState();
202            } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
203                notifyWifiAPState();
204            } else {
205                return;
206            }
207        }
208    }
209
210    /**
211     * A wrapper of a broadcast receiver which provides download manager information.
212     */
213    private class DownloadReceiver extends BroadcastReceiver {
214        /**
215         * {@inheritDoc}
216         */
217        @Override
218        public void onReceive(Context context, Intent intent) {
219            String action = intent.getAction();
220            Log.v("DownloadReceiver", "onReceive() is called with " + intent);
221            // Download complete
222            if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
223                notifiyDownloadState();
224            }
225        }
226    }
227
228    private class WifiServiceHandler extends Handler {
229        /**
230         * {@inheritDoc}
231         */
232        @Override
233        public void handleMessage(Message msg) {
234            switch (msg.what) {
235                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
236                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
237                        // AsyncChannel in msg.obj
238                    } else {
239                        Log.v(LOG_TAG, "Failed to establish AsyncChannel connection");
240                    }
241                    break;
242                default:
243                    // Ignore
244                    break;
245            }
246        }
247    }
248
249    /**
250     * Initialize all the network states.
251     */
252    public void initializeNetworkStates() {
253        // For each network type, initialize network states to UNKNOWN, and no verification
254        // flag is set.
255        for (int networkType = NUM_NETWORK_TYPES - 1; networkType >= 0; networkType--) {
256            mConnectivityState[networkType] =  new NetworkState();
257            Log.v(LOG_TAG, "Initialize network state for " + networkType + ": " +
258                    mConnectivityState[networkType].toString());
259        }
260    }
261
262    public void recordNetworkState(int networkType, State networkState) {
263        // deposit a network state
264        Log.v(LOG_TAG, "record network state for network " +  networkType +
265                ", state is " + networkState);
266        mConnectivityState[networkType].recordState(networkState);
267    }
268
269    /**
270     * Set the state transition criteria
271     *
272     * @param networkType
273     * @param initState
274     * @param transitionDir
275     * @param targetState
276     */
277    public void setStateTransitionCriteria(int networkType, State initState,
278            StateTransitionDirection transitionDir, State targetState) {
279        mConnectivityState[networkType].setStateTransitionCriteria(
280                initState, transitionDir, targetState);
281    }
282
283    /**
284     * Validate the states recorded.
285     * @param networkType
286     * @return
287     */
288    public boolean validateNetworkStates(int networkType) {
289        Log.v(LOG_TAG, "validate network state for " + networkType + ": ");
290        return mConnectivityState[networkType].validateStateTransition();
291    }
292
293    /**
294     * Fetch the failure reason for the transition.
295     * @param networkType
296     * @return result from network state validation
297     */
298    public String getTransitionFailureReason(int networkType) {
299        Log.v(LOG_TAG, "get network state transition failure reason for " + networkType + ": " +
300                mConnectivityState[networkType].toString());
301        return mConnectivityState[networkType].getFailureReason();
302    }
303
304    /**
305     * Send a notification via the mConnectivityMonitor when the network connectivity changes.
306     */
307    private void notifyNetworkConnectivityChange() {
308        synchronized(mConnectivityMonitor) {
309            Log.v(LOG_TAG, "notify network connectivity changed");
310            mConnectivityMonitor.notifyAll();
311        }
312    }
313
314    /**
315     * Send a notification when a scan for the wifi network is done.
316     */
317    private void notifyScanResult() {
318        synchronized (this) {
319            Log.v(LOG_TAG, "notify that scan results are available");
320            this.notify();
321        }
322    }
323
324    /**
325     * Send a notification via the mWifiMonitor when the wifi state changes.
326     */
327    private void notifyWifiState() {
328        synchronized (mWifiMonitor) {
329            Log.v(LOG_TAG, "notify wifi state changed.");
330            mWifiMonitor.notify();
331        }
332    }
333
334    /**
335     * Send a notification via the mDownloadMonitor when a download is complete.
336     */
337    private void notifiyDownloadState() {
338        synchronized (mDownloadMonitor) {
339            Log.v(LOG_TAG, "notifiy download manager state changed.");
340            mDownloadMonitor.notify();
341        }
342    }
343
344    /**
345     * Send a notification when the wifi ap state changes.
346     */
347    private void notifyWifiAPState() {
348        synchronized (this) {
349            Log.v(LOG_TAG, "notify wifi AP state changed.");
350            this.notify();
351        }
352    }
353
354    /**
355     * Start a download on a given url and wait for completion.
356     *
357     * @param targetUrl the target to download.x
358     * @param timeout to wait for download to finish
359     * @return true if we successfully downloaded the requestedUrl, false otherwise.
360     */
361    public boolean startDownloadAndWait(String targetUrl, long timeout) {
362        if (targetUrl.length() == 0 || targetUrl == null) {
363            Log.v(LOG_TAG, "Empty or Null target url requested to DownloadManager");
364            return true;
365        }
366        Request request = new Request(Uri.parse(targetUrl));
367        long enqueue = mDownloadManager.enqueue(request);
368        Log.v(LOG_TAG, "Sending download request of " + targetUrl + " to DownloadManager");
369        long startTime = System.currentTimeMillis();
370        while (true) {
371            if ((System.currentTimeMillis() - startTime) > timeout) {
372                Log.v(LOG_TAG, "startDownloadAndWait timed out, failed to fetch " + targetUrl +
373                        " within " + timeout);
374                return downloadSuccessful(enqueue);
375            }
376            Log.v(LOG_TAG, "Waiting for the download to finish " + targetUrl);
377            synchronized (mDownloadMonitor) {
378                try {
379                    mDownloadMonitor.wait(SHORT_TIMEOUT);
380                } catch (InterruptedException e) {
381                    e.printStackTrace();
382                }
383                if (!downloadSuccessful(enqueue)) {
384                    continue;
385                }
386                return true;
387            }
388        }
389    }
390
391    /**
392     * Fetch the Download Manager's UID.
393     * @return the Download Manager's UID
394     */
395    public int downloadManagerUid() {
396        try {
397            PackageManager pm = mContext.getPackageManager();
398            ApplicationInfo appInfo = pm.getApplicationInfo(DOWNLOAD_MANAGER_PKG_NAME,
399                    PackageManager.GET_META_DATA);
400            return appInfo.uid;
401        } catch (NameNotFoundException e) {
402            Log.d(LOG_TAG, "Did not find the package for the download service.");
403            return -1;
404        }
405    }
406
407    /**
408     * Determines if a given download was successful by querying the DownloadManager.
409     *
410     * @param enqueue the id used to identify/query the DownloadManager with.
411     * @return true if download was successful, false otherwise.
412     */
413    private boolean downloadSuccessful(long enqueue) {
414        Query query = new Query();
415        query.setFilterById(enqueue);
416        Cursor c = mDownloadManager.query(query);
417        if (c.moveToFirst()) {
418            int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
419            if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
420                Log.v(LOG_TAG, "Successfully downloaded file!");
421                return true;
422            }
423        }
424        return false;
425    }
426
427    /**
428     * Wait for network connectivity state.
429     * @param networkType the network to check for
430     * @param expectedState the desired state
431     * @param timeout in milliseconds
432     * @return true if the network connectivity state matched what was expected
433     */
434    public boolean waitForNetworkState(int networkType, State expectedState, long timeout) {
435        long startTime = System.currentTimeMillis();
436        while (true) {
437            if ((System.currentTimeMillis() - startTime) > timeout) {
438                Log.v(LOG_TAG, "waitForNetworkState time out, the state of network type " + networkType +
439                        " is: " + mCM.getNetworkInfo(networkType).getState());
440                if (mCM.getNetworkInfo(networkType).getState() != expectedState) {
441                    return false;
442                } else {
443                    // the broadcast has been sent out. the state has been changed.
444                    Log.v(LOG_TAG, "networktype: " + networkType + " state: " +
445                            mCM.getNetworkInfo(networkType));
446                    return true;
447                }
448            }
449            Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType +
450                    " to be " + expectedState.toString());
451            synchronized (mConnectivityMonitor) {
452                try {
453                    mConnectivityMonitor.wait(SHORT_TIMEOUT);
454                } catch (InterruptedException e) {
455                    e.printStackTrace();
456                }
457                if (mNetworkInfo == null) {
458                    Log.v(LOG_TAG, "Do not have networkInfo! Force fetch of network info.");
459                    mNetworkInfo = mCM.getActiveNetworkInfo();
460                }
461                // Still null after force fetch? Maybe the network did not have time to be brought
462                // up yet.
463                if (mNetworkInfo == null) {
464                    Log.v(LOG_TAG, "Failed to force fetch networkInfo. " +
465                            "The network is still not ready. Wait for the next broadcast");
466                    continue;
467                }
468                if ((mNetworkInfo.getType() != networkType) ||
469                        (mNetworkInfo.getState() != expectedState)) {
470                    Log.v(LOG_TAG, "network state for " + mNetworkInfo.getType() +
471                            "is: " + mNetworkInfo.getState());
472                    continue;
473                }
474                return true;
475            }
476        }
477    }
478
479    /**
480     * Wait for a given wifi state to occur within a given timeout.
481     * @param expectedState the expected wifi state.
482     * @param timeout for the state to be set in milliseconds.
483     * @return true if the state was achieved within the timeout, false otherwise.
484     */
485    public boolean waitForWifiState(int expectedState, long timeout) {
486        // Wait for Wifi state: WIFI_STATE_DISABLED, WIFI_STATE_DISABLING, WIFI_STATE_ENABLED,
487        //                      WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN
488        long startTime = System.currentTimeMillis();
489        while (true) {
490            if ((System.currentTimeMillis() - startTime) > timeout) {
491                if (mWifiState != expectedState) {
492                    return false;
493                } else {
494                    return true;
495                }
496            }
497            Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState);
498            synchronized (mWifiMonitor) {
499                try {
500                    mWifiMonitor.wait(SHORT_TIMEOUT);
501                } catch (InterruptedException e) {
502                    e.printStackTrace();
503                }
504                if (mWifiState != expectedState) {
505                    Log.v(LOG_TAG, "Wifi state is: " + mWifiState);
506                    continue;
507                }
508                return true;
509            }
510        }
511    }
512
513    /**
514     * Convenience method to determine if we are connected to a mobile network.
515     * @return true if connected to a mobile network, false otherwise.
516     */
517    public boolean isConnectedToMobile() {
518        NetworkInfo networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
519        return networkInfo.isConnected();
520    }
521
522    /**
523     * Convenience method to determine if we are connected to wifi.
524     * @return true if connected to wifi, false otherwise.
525     */
526    public boolean isConnectedToWifi() {
527        NetworkInfo networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
528        return networkInfo.isConnected();
529    }
530
531    /**
532     * Associate the device to given SSID
533     * If the device is already associated with a WiFi, disconnect and forget it,
534     * We don't verify whether the connection is successful or not, leave this to the test
535     */
536    public boolean connectToWifi(String knownSSID) {
537        WifiConfiguration config = new WifiConfiguration();
538        config.SSID = knownSSID;
539        config.allowedKeyManagement.set(KeyMgmt.NONE);
540        return connectToWifiWithConfiguration(config);
541    }
542
543    /**
544     * Connect to Wi-Fi with the given configuration.
545     * @param config
546     * @return true if we are connected to a given AP.
547     */
548    public boolean connectToWifiWithConfiguration(WifiConfiguration config) {
549        //  The SSID in the configuration is a pure string, need to convert it to a quoted string.
550        String ssid = config.SSID;
551        config.SSID = convertToQuotedString(ssid);
552
553        // If wifi is not enabled, enable it
554        if (!mWifiManager.isWifiEnabled()) {
555            Log.v(LOG_TAG, "Wifi is not enabled, enable it");
556            mWifiManager.setWifiEnabled(true);
557            // wait for the wifi state change before start scanning.
558            if (!waitForWifiState(WifiManager.WIFI_STATE_ENABLED, 2 * SHORT_TIMEOUT)) {
559                Log.v(LOG_TAG, "Wait for WIFI_STATE_ENABLED failed");
560                return false;
561            }
562        }
563
564        boolean foundApInScanResults = false;
565        for (int retry = 0; retry < 5; retry++) {
566            List<ScanResult> netList = mWifiManager.getScanResults();
567            if (netList != null) {
568                Log.v(LOG_TAG, "size of scan result list: " + netList.size());
569                for (int i = 0; i < netList.size(); i++) {
570                    ScanResult sr= netList.get(i);
571                    if (sr.SSID.equals(ssid)) {
572                        Log.v(LOG_TAG, "Found " + ssid + " in the scan result list.");
573                        Log.v(LOG_TAG, "Retry: " + retry);
574                        foundApInScanResults = true;
575                        mWifiManager.connect(config, new WifiManager.ActionListener() {
576                                public void onSuccess() {
577                                }
578                                public void onFailure(int reason) {
579                                    Log.e(LOG_TAG, "connect failed " + reason);
580                                }
581                            });
582
583                        break;
584                    }
585                }
586            }
587            if (foundApInScanResults) {
588                return true;
589            } else {
590                // Start an active scan
591                mWifiManager.startScan();
592                mScanResultIsAvailable = false;
593                long startTime = System.currentTimeMillis();
594                while (!mScanResultIsAvailable) {
595                    if ((System.currentTimeMillis() - startTime) > WIFI_SCAN_TIMEOUT) {
596                        Log.v(LOG_TAG, "wait for scan results timeout");
597                        return false;
598                    }
599                    // wait for the scan results to be available
600                    synchronized (this) {
601                        // wait for the scan result to be available
602                        try {
603                            this.wait(WAIT_FOR_SCAN_RESULT);
604                        } catch (InterruptedException e) {
605                            e.printStackTrace();
606                        }
607                        if ((mWifiManager.getScanResults() == null) ||
608                                (mWifiManager.getScanResults().size() <= 0)) {
609                            continue;
610                        }
611                        mScanResultIsAvailable = true;
612                    }
613                }
614            }
615        }
616        return false;
617    }
618
619    /*
620     * Disconnect from the current AP and remove configured networks.
621     */
622    public boolean disconnectAP() {
623        // remove saved networks
624        List<WifiConfiguration> wifiConfigList = mWifiManager.getConfiguredNetworks();
625        Log.v(LOG_TAG, "size of wifiConfigList: " + wifiConfigList.size());
626        for (WifiConfiguration wifiConfig: wifiConfigList) {
627            Log.v(LOG_TAG, "Remove wifi configuration: " + wifiConfig.networkId);
628            int netId = wifiConfig.networkId;
629            mWifiManager.forget(netId, new WifiManager.ActionListener() {
630                    public void onSuccess() {
631                    }
632                    public void onFailure(int reason) {
633                        Log.e(LOG_TAG, "forget failed " + reason);
634                    }
635                });
636        }
637        return true;
638    }
639
640    /**
641     * Enable Wifi
642     * @return true if Wifi is enabled successfully
643     */
644    public boolean enableWifi() {
645        return mWifiManager.setWifiEnabled(true);
646    }
647
648    /**
649     * Disable Wifi
650     * @return true if Wifi is disabled successfully
651     */
652    public boolean disableWifi() {
653        return mWifiManager.setWifiEnabled(false);
654    }
655
656    /**
657     * Remove configured networks and disable wifi
658     */
659    public boolean removeConfiguredNetworksAndDisableWifi() {
660        if (!disconnectAP()) {
661            return false;
662        }
663        sleep(SHORT_TIMEOUT);
664        if (!mWifiManager.setWifiEnabled(false)) {
665            return false;
666        }
667        sleep(SHORT_TIMEOUT);
668        return true;
669    }
670
671    /**
672     * Make the current thread sleep.
673     * @param sleeptime the time to sleep in milliseconds
674     */
675    private void sleep(long sleeptime) {
676        try {
677            Thread.sleep(sleeptime);
678        } catch (InterruptedException e) {}
679    }
680
681    /**
682     * Set airplane mode on device, caller is responsible to ensuring correct state.
683     * @param context {@link Context}
684     * @param enableAM to enable or disable airplane mode.
685     */
686    public void setAirplaneMode(Context context, boolean enableAM) {
687        //set the airplane mode
688        Settings.System.putInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
689                enableAM ? 1 : 0);
690        // Post the intent
691        Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
692        intent.putExtra("state", enableAM);
693        context.sendBroadcast(intent);
694    }
695
696    /**
697     * Add quotes around the string.
698     * @param string to convert
699     * @return string with quotes around it
700     */
701    protected static String convertToQuotedString(String string) {
702        return "\"" + string + "\"";
703    }
704
705    public void cleanUp() {
706        // Unregister receivers if defined.
707        if (mConnectivityReceiver != null) {
708            mContext.unregisterReceiver(mConnectivityReceiver);
709        }
710        if (mWifiReceiver != null) {
711            mContext.unregisterReceiver(mWifiReceiver);
712        }
713        if (mDownloadReceiver != null) {
714            mContext.unregisterReceiver(mDownloadReceiver);
715        }
716        Log.v(LOG_TAG, "onDestroy, inst=" + Integer.toHexString(hashCode()));
717    }
718
719    /**
720     * Helper method used to test data connectivity by pinging a series of popular sites.
721     * @return true if device has data connectivity, false otherwise.
722     */
723    public boolean hasData() {
724        String[] hostList = {"www.google.com", "www.yahoo.com",
725                "www.bing.com", "www.facebook.com", "www.ask.com"};
726        try {
727            for (int i = 0; i < hostList.length; ++i) {
728                String host = hostList[i];
729                Process p = Runtime.getRuntime().exec("ping -c 10 -w 100 " + host);
730                int status = p.waitFor();
731                if (status == 0) {
732                    return true;
733                }
734            }
735        } catch (UnknownHostException e) {
736            Log.e(LOG_TAG, "Ping test Failed: Unknown Host");
737        } catch (IOException e) {
738            Log.e(LOG_TAG, "Ping test Failed: IOException");
739        } catch (InterruptedException e) {
740            Log.e(LOG_TAG, "Ping test Failed: InterruptedException");
741        }
742        return false;
743    }
744}
745