Home | History | Annotate | Download | only in util
      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 
     17 package com.android.bandwidthtest.util;
     18 
     19 import android.app.DownloadManager;
     20 import android.app.DownloadManager.Query;
     21 import android.app.DownloadManager.Request;
     22 import android.content.BroadcastReceiver;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.IntentFilter;
     26 import android.content.pm.ApplicationInfo;
     27 import android.content.pm.PackageManager;
     28 import android.content.pm.PackageManager.NameNotFoundException;
     29 import android.database.Cursor;
     30 import android.net.ConnectivityManager;
     31 import android.net.NetworkInfo;
     32 import android.net.NetworkInfo.State;
     33 import android.net.Uri;
     34 import android.net.wifi.ScanResult;
     35 import android.net.wifi.WifiConfiguration;
     36 import android.net.wifi.WifiConfiguration.KeyMgmt;
     37 import android.net.wifi.WifiManager;
     38 import android.os.Handler;
     39 import android.os.Message;
     40 import android.provider.Settings;
     41 import android.util.Log;
     42 
     43 import com.android.bandwidthtest.NetworkState;
     44 import com.android.bandwidthtest.NetworkState.StateTransitionDirection;
     45 import com.android.internal.util.AsyncChannel;
     46 
     47 import junit.framework.Assert;
     48 
     49 import java.io.IOException;
     50 import java.net.UnknownHostException;
     51 import java.util.List;
     52 
     53 /*
     54  * Utility class used to set the connectivity of the device and to download files.
     55  */
     56 public 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