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